diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3817efbdc5c5a957a02da4b5085836a0ae29c3b4..b28a26b5328bca94654ab91410814b6052e32963 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -212,6 +212,7 @@ SET( libringclient_LIB_SRCS
   src/video/sourcemodel.cpp
   src/video/channel.cpp
   src/video/resolution.cpp
+  src/video/configurationproxy.cpp
   src/audio/alsapluginmodel.cpp
   src/audio/inputdevicemodel.cpp
   src/audio/managermodel.cpp
@@ -333,6 +334,7 @@ SET(libringclient_video_LIB_HDRS
   src/video/channel.h
   src/video/rate.h
   src/video/previewmanager.h
+  src/video/configurationproxy.h
   #The renderer implementations are not exported on purpose
 )
 
@@ -461,6 +463,7 @@ SET(libringclient_PRIVATE_HDRS
    src/private/phonedirectorymodel_p.h
    src/private/instantmessagingmodel_p.h
    src/private/videorenderer_p.h
+   src/private/videodevice_p.h
    src/private/collectionmodel_p.h
    src/private/securityflaw_p.h
    src/collectioncreationinterface.h
diff --git a/src/account.cpp b/src/account.cpp
index 76030a03161e93b834a26cf38773b9836dc849e9..d4cbe22227505c792caa66c3d584c6e8dada5814 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -281,8 +281,13 @@ const QString AccountPrivate::accountDetail(const QString& param) const
       if (param == DRing::Account::ConfProperties::Registration::STATUS) { //If an account is new, then it is unregistered
          return DRing::Account::States::UNREGISTERED;
       }
-      if (q_ptr->protocol() != Account::Protocol::IAX) //IAX accounts lack some fields, be quiet
-         qDebug() << "Account parameter \"" << param << "\" not found";
+      if (q_ptr->protocol() != Account::Protocol::IAX) {//IAX accounts lack some fields, be quiet
+         static QHash<QString,bool> alreadyWarned;
+         if (!alreadyWarned[param]) {
+            alreadyWarned[param] = true;
+            qDebug() << "Account parameter \"" << param << "\" not found";
+         }
+      }
       return QString();
    }
    else {
diff --git a/src/categorizedcontactmodel.cpp b/src/categorizedcontactmodel.cpp
index d4805e3a1877e40e543390d0f2baaaf0bbefe54e..f3d6ebcd6cd0f78ea2d3e39c4d456b2ddeaf2497 100644
--- a/src/categorizedcontactmodel.cpp
+++ b/src/categorizedcontactmodel.cpp
@@ -49,7 +49,7 @@ public:
    };
 
    //Constructor
-   ContactTreeNode( Person* ct          , CategorizedContactModel* parent);
+   ContactTreeNode( const Person* ct    , CategorizedContactModel* parent);
    ContactTreeNode( ContactMethod* cm   , CategorizedContactModel* parent);
    ContactTreeNode( const QString& name , CategorizedContactModel* parent);
    virtual ~ContactTreeNode();
@@ -57,14 +57,14 @@ public:
    virtual QObject* getSelf() const override;
 
    //Attributes
-   Person* m_pContact;
+   const Person*             m_pContact      ;
    ContactMethod*            m_pContactMethod;
    uint                      m_Index         ;
    QString                   m_Name          ;
    NodeType                  m_Type          ;
    ContactTreeNode*          m_pParent       ;
    QVector<ContactTreeNode*> m_lChildren     ;
-   CategorizedContactModel*        m_pModel        ;
+   CategorizedContactModel*  m_pModel        ;
 
    //Helpers
    void slotChanged                        (       );
@@ -87,6 +87,8 @@ public:
    QHash<QString,ContactTreeNode*> m_hCategories      ;
    int                             m_Role             ;
    QStringList                     m_lMimes           ;
+   bool                            m_SortAlphabetical ;
+   QString                         m_DefaultCategory  ;
 
    //Helper
    ContactTreeNode* getContactTopLevelItem(const QString& category);
@@ -97,10 +99,10 @@ private:
 
 public Q_SLOTS:
    void reloadCategories();
-   void slotContactAdded(Person* c);
+   void slotContactAdded(const Person* c);
 };
 
-ContactTreeNode::ContactTreeNode(Person* ct, CategorizedContactModel* parent) : CategorizedCompositeNode(CategorizedCompositeNode::Type::CONTACT),
+ContactTreeNode::ContactTreeNode(const Person* ct, CategorizedContactModel* parent) : CategorizedCompositeNode(CategorizedCompositeNode::Type::CONTACT),
    m_pContact(ct),m_Index(-1),m_pContactMethod(nullptr),m_Type(ContactTreeNode::NodeType::PERSON),m_pParent(nullptr),m_pModel(parent)
 {
    QObject::connect(m_pContact,&Person::changed                      ,[this](            ){ slotChanged                        (   ); });
@@ -130,7 +132,7 @@ QModelIndex CategorizedContactModelPrivate::getIndex(int row, int column, Contac
 
 QObject* ContactTreeNode::getSelf() const
 {
-   return m_pContact;
+   return (QObject*)m_pContact;
 }
 
 void ContactTreeNode::slotChanged()
@@ -173,7 +175,7 @@ void ContactTreeNode::slotContactMethodCountAboutToChange(int count, int oldCoun
 }
 
 CategorizedContactModelPrivate::CategorizedContactModelPrivate(CategorizedContactModel* parent) : QObject(parent), q_ptr(parent),
-m_lCategoryCounter(),m_Role(Qt::DisplayRole)
+m_lCategoryCounter(),m_Role(Qt::DisplayRole),m_SortAlphabetical(true)
 {
 
 }
@@ -186,7 +188,7 @@ CategorizedContactModel::CategorizedContactModel(int role) : QAbstractItemModel(
    d_ptr->m_lCategoryCounter.reserve(32);
    d_ptr->m_lMimes << RingMimes::PLAIN_TEXT << RingMimes::PHONENUMBER;
 
-   connect(PersonModel::instance(),SIGNAL(newPersonAdded(Person*)),d_ptr.data(),SLOT(slotContactAdded(Person*)));
+   connect(PersonModel::instance(),&PersonModel::newPersonAdded,d_ptr.data(),&CategorizedContactModelPrivate::slotContactAdded);
 
    for(int i=0; i < PersonModel::instance()->rowCount();i++) {
       Person* p = qvariant_cast<Person*>(PersonModel::instance()->index(i,0).data((int)Person::Role::Object));
@@ -256,9 +258,10 @@ void CategorizedContactModelPrivate::reloadCategories()
    emit q_ptr->layoutChanged();
 }
 
-void CategorizedContactModelPrivate::slotContactAdded(Person* c)
+void CategorizedContactModelPrivate::slotContactAdded(const Person* c)
 {
    if (!c) return;
+
    const QString val = category(c);
    ContactTreeNode* item = getContactTopLevelItem(val);
    ContactTreeNode* contactNode = new ContactTreeNode(c,q_ptr);
@@ -399,7 +402,7 @@ Qt::ItemFlags CategorizedContactModel::flags( const QModelIndex& index ) const
 
    const ContactTreeNode* modelNode = static_cast<ContactTreeNode*>(index.internalPointer());
 
-   return (modelNode->m_pContact && modelNode->m_pContact->isActive() ? Qt::NoItemFlags : Qt::ItemIsEnabled)
+   return (((!modelNode->m_pContact) || (modelNode->m_pContact->isActive())) ? Qt::ItemIsEnabled : Qt::NoItemFlags )
       | Qt::ItemIsSelectable
       | (modelNode->m_pParent? (Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled) : Qt::ItemIsEnabled
    );
@@ -501,8 +504,10 @@ QString CategorizedContactModelPrivate::category(const Person* ct) const {
 
    QString cat = ct->roleData(m_Role).toString();
 
-   if (cat.size())
+   if (cat.size() && m_SortAlphabetical)
       cat = cat[0].toUpper();
+   else if (!cat.size())
+      cat = m_DefaultCategory;
 
    return cat;
 }
@@ -515,4 +520,24 @@ void CategorizedContactModel::setRole(int role)
    }
 }
 
+void CategorizedContactModel::setSortAlphabetical(bool alpha)
+{
+   d_ptr->m_SortAlphabetical = alpha;
+}
+
+bool CategorizedContactModel::isSortAlphabetical() const
+{
+   return d_ptr->m_SortAlphabetical;
+}
+
+void CategorizedContactModel::setDefaultCategory(const QString& cat)
+{
+   d_ptr->m_DefaultCategory = cat;
+}
+
+QString CategorizedContactModel::defaultCategory() const
+{
+   return d_ptr->m_DefaultCategory;
+}
+
 #include <categorizedcontactmodel.moc>
diff --git a/src/categorizedcontactmodel.h b/src/categorizedcontactmodel.h
index 4ed5680ef473add7345f1b5bbe1828e3bc2b9733..b1b7664a3a16170d873a0ba8e2b9ec5c978ce847 100644
--- a/src/categorizedcontactmodel.h
+++ b/src/categorizedcontactmodel.h
@@ -46,6 +46,8 @@ public:
 
    //Setters
    void setRole(int role);
+   void setSortAlphabetical(bool alpha = true);
+   void setDefaultCategory(const QString& cat);
 
    //Model implementation
    virtual bool          setData     ( const QModelIndex& index, const QVariant &value, int role   ) override;
@@ -62,7 +64,9 @@ public:
    virtual QHash<int,QByteArray> roleNames() const override;
 
    //Getter
-   static int acceptedPayloadTypes();
+   static int acceptedPayloadTypes()      ;
+   bool       isSortAlphabetical  () const;
+   QString    defaultCategory     () const;
 
 private:
    QScopedPointer<CategorizedContactModelPrivate> d_ptr;
diff --git a/src/categorizedhistorymodel.cpp b/src/categorizedhistorymodel.cpp
index cc6fc86612dee1aff7bd756162b773aa27ba5c49..12a4a9675bd80d87e68aab71f1ce5d7d3ba0fd23 100644
--- a/src/categorizedhistorymodel.cpp
+++ b/src/categorizedhistorymodel.cpp
@@ -632,7 +632,6 @@ bool CategorizedHistoryModel::addItemCallback(const Call* item)
 
 bool CategorizedHistoryModel::removeItemCallback(const Call* item)
 {
-   Q_UNUSED(item)
    emit const_cast<Call*>(item)->changed();
    return false;
 }
diff --git a/src/collectioninterface.hpp b/src/collectioninterface.hpp
index 29a66714882bf429a5e439649074c417616ea650..3ea552c1930ae194ddfa27a8f0eb28cbb68850a5 100644
--- a/src/collectioninterface.hpp
+++ b/src/collectioninterface.hpp
@@ -22,6 +22,7 @@ template<typename T> class ItemBase;
 
 class CollectionInterfacePrivate {
 public:
+   CollectionInterfacePrivate() : m_pParent(nullptr){}
 
    ///The backend parent of nullptr
    CollectionInterface*          m_pParent    ;
diff --git a/src/collectionmodel.cpp b/src/collectionmodel.cpp
index a9508cfec1a0786f22a1678ec24abca9a61ba909..0898b5769c21c877c3590e78bd499100f14e60c0 100644
--- a/src/collectionmodel.cpp
+++ b/src/collectionmodel.cpp
@@ -119,7 +119,7 @@ QVariant CollectionModel::data (const QModelIndex& idx, int role) const
                break;
             case Qt::CheckStateRole: {
                if (ItemModelStateSerializationDelegate::instance()) //TODO do better than that
-                  return ItemModelStateSerializationDelegate::instance()->isChecked(item->collection)?Qt::Checked:Qt::Unchecked;
+                  return item->collection->isEnabled()?Qt::Checked:Qt::Unchecked;
             }
             case static_cast<int>(Role::hasManageableChildren):
                return item->manageableCount ? true : false;
@@ -220,7 +220,7 @@ QModelIndex CollectionModel::parent( const QModelIndex& idx ) const
 
 QModelIndex CollectionModel::index( int row, int column, const QModelIndex& parent ) const
 {
-   if (parent.isValid()) {
+   if (parent.isValid() && parent.model() == this && row < rowCount(parent)) {
       CollectionModelPrivate::ProxyItem* parentItem = static_cast<CollectionModelPrivate::ProxyItem*>(parent.internalPointer());
       CollectionModelPrivate::ProxyItem* item = nullptr;
       if (row < parentItem->m_Children.size())
diff --git a/src/delegates/itemmodelstateserializationdelegate.h b/src/delegates/itemmodelstateserializationdelegate.h
index 2f7feabb151ba129cac19d8c2dc02f02c1f25037..1a13374d2f4d3a6829e201b308470b31f7e08dbb 100644
--- a/src/delegates/itemmodelstateserializationdelegate.h
+++ b/src/delegates/itemmodelstateserializationdelegate.h
@@ -34,10 +34,10 @@ public:
    static ItemModelStateSerializationDelegate* instance();
 
    //Getter
-   virtual bool isChecked(CollectionInterface* backend) const = 0;
+   virtual bool isChecked(const CollectionInterface* backend) const = 0;
 
    //Setter
-   virtual bool setChecked(CollectionInterface* backend, bool enabled) = 0;
+   virtual bool setChecked(const CollectionInterface* backend, bool enabled) = 0;
 
 private:
    static ItemModelStateSerializationDelegate* m_spInstance;
diff --git a/src/fallbackpersoncollection.cpp b/src/fallbackpersoncollection.cpp
index c5c30c1f2341c7c3fdbddf6d1d9ff1d44e7630a2..43ddc4a6da6df399621f25dfb06ac3114826a62b 100644
--- a/src/fallbackpersoncollection.cpp
+++ b/src/fallbackpersoncollection.cpp
@@ -24,6 +24,7 @@
 #include <QtCore/QHash>
 #include <QtCore/QTimer>
 #include <QtCore/QUrl>
+#include <QtCore/QCryptographicHash>
 #include <QtWidgets/QApplication>
 #include <QtCore/QStandardPaths>
 
@@ -34,6 +35,7 @@
 #include "contactmethod.h"
 #include "collectioneditor.h"
 #include "delegates/pixmapmanipulationdelegate.h"
+#include "delegates/itemmodelstateserializationdelegate.h"
 
 
 class FallbackPersonBackendEditor : public CollectionEditor<Person>
@@ -46,8 +48,9 @@ public:
    virtual bool addNew     ( const Person* item ) override;
    virtual bool addExisting( const Person* item ) override;
 
-   QVector<Person*> m_lItems;
-   QString          m_Path  ;
+   QVector<Person*>             m_lItems;
+   QString                      m_Path  ;
+   QHash<const Person*,QString> m_hPaths;
 
 private:
    virtual QVector<Person*> items() const override;
@@ -70,6 +73,12 @@ public Q_SLOTS:
 
 FallbackPersonCollectionPrivate::FallbackPersonCollectionPrivate(FallbackPersonCollection* parent, CollectionMediator<Person>* mediator, const QString& path) : q_ptr(parent), m_pMediator(mediator), m_Path(path)
 {
+   //Default to somewhere ~/.local/share
+   if (m_Path.isEmpty()) {
+      m_Path = (QStandardPaths::writableLocation(QStandardPaths::DataLocation)) + "/vCard/";
+      static_cast<FallbackPersonBackendEditor*>(q_ptr->editor<Person>())->m_Path = m_Path;
+   }
+
    m_Name = path.split('/').last();
    if (m_Name.size())
       m_Name[0] = m_Name[0].toUpper();
@@ -89,6 +98,25 @@ FallbackPersonCollection::~FallbackPersonCollection()
 
 bool FallbackPersonBackendEditor::save(const Person* item)
 {
+   if (!item)
+      return false;
+
+   //An UID is required
+   if (item->uid().isEmpty()) {
+      QCryptographicHash hash(QCryptographicHash::Sha1);
+      for (ContactMethod* n : item->phoneNumbers())
+         hash.addData(n->uri().toLatin1());
+      hash.addData(item->formattedName().toLatin1());
+      QByteArray random;
+
+      for (int i=0;i<5;i++)
+         random.append(QChar((char)(rand()%255)));
+
+      hash.addData(random);
+
+      const_cast<Person*>(item)->setUid(hash.result().toHex());
+   }
+
    QFile file(m_Path+'/'+item->uid()+".vcf");
    file.open(QIODevice::WriteOnly);
    file.write(item->toVCard({}));
@@ -98,8 +126,21 @@ bool FallbackPersonBackendEditor::save(const Person* item)
 
 bool FallbackPersonBackendEditor::remove(const Person* item)
 {
-   Q_UNUSED(item)
-   return false;
+   if (!item)
+      return false;
+
+   QString path = m_hPaths[item];
+
+   if (path.isEmpty())
+      path = m_Path+'/'+item->uid()+".vcf";
+
+   bool ret = QFile::remove(path);
+
+   if (ret) {
+      ret &= mediator()->removeItem(item);
+   }
+
+   return ret;
 }
 
 bool FallbackPersonBackendEditor::edit( Person* item)
@@ -110,8 +151,13 @@ bool FallbackPersonBackendEditor::edit( Person* item)
 
 bool FallbackPersonBackendEditor::addNew(const Person* item)
 {
-   Q_UNUSED(item)
-   return false;
+   bool ret = save(item);
+
+   if (ret) {
+      addExisting(item);
+   }
+
+   return ret;
 }
 
 bool FallbackPersonBackendEditor::addExisting(const Person* item)
@@ -133,7 +179,7 @@ QString FallbackPersonCollection::name () const
 
 QString FallbackPersonCollection::category () const
 {
-   return QObject::tr("Contacts");
+   return QObject::tr("Contact");
 }
 
 QVariant FallbackPersonCollection::icon() const
@@ -143,14 +189,15 @@ QVariant FallbackPersonCollection::icon() const
 
 bool FallbackPersonCollection::isEnabled() const
 {
-   return true;
+   return ItemModelStateSerializationDelegate::instance()->isChecked(this);
 }
 
 bool FallbackPersonCollection::load()
 {
    bool ok;
-   QList< Person* > ret =  VCardUtils::loadDir(QUrl(d_ptr->m_Path),ok);
+   QList< Person* > ret =  VCardUtils::loadDir(QUrl(d_ptr->m_Path),ok,static_cast<FallbackPersonBackendEditor*>(editor<Person>())->m_hPaths);
    for(Person* p : ret) {
+      p->setCollection(this);
       editor<Person>()->addExisting(p);
    }
 
@@ -172,7 +219,7 @@ CollectionInterface::SupportedFeatures FallbackPersonCollection::supportedFeatur
       CollectionInterface::SupportedFeatures::LOAD       |
       CollectionInterface::SupportedFeatures::CLEAR      |
       CollectionInterface::SupportedFeatures::MANAGEABLE |
-//       CollectionInterface::SupportedFeatures::REMOVE|
+      CollectionInterface::SupportedFeatures::REMOVE     |
       CollectionInterface::SupportedFeatures::ADD        );
 }
 
@@ -186,7 +233,7 @@ bool FallbackPersonCollection::clear()
 
 QByteArray FallbackPersonCollection::id() const
 {
-   return "fpc2";
+   return "fpc2"+d_ptr->m_Path.toLatin1();
 }
 
 
@@ -194,7 +241,12 @@ void FallbackPersonCollectionPrivate::loadAsync()
 {
    QDir d(m_Path);
    for (const QString& dir : d.entryList(QDir::AllDirs)) {
-      PersonModel::instance()->addCollection<FallbackPersonCollection,QString,FallbackPersonCollection*>(m_Path+'/'+dir,q_ptr);
+      if (dir != QString('.') && dir != "..") {
+         CollectionInterface* col = PersonModel::instance()->addCollection<FallbackPersonCollection,QString,FallbackPersonCollection*>(m_Path+'/'+dir,q_ptr);
+         if (ItemModelStateSerializationDelegate::instance()->isChecked(col)) {
+            col->load();
+         }
+      }
    }
 }
 
diff --git a/src/fallbackpersoncollection.h b/src/fallbackpersoncollection.h
index 278c11a552823139977513fd00a4540a73651426..956e9e806e1eeb957b82a4fa039d4ec3cb8be726 100644
--- a/src/fallbackpersoncollection.h
+++ b/src/fallbackpersoncollection.h
@@ -37,7 +37,7 @@ template<typename T> class CollectionMediator;
 class LIB_EXPORT FallbackPersonCollection : public CollectionInterface
 {
 public:
-   explicit FallbackPersonCollection(CollectionMediator<Person>* mediator, const QString& path = "/tmp/vcard", FallbackPersonCollection* parent = nullptr);
+   explicit FallbackPersonCollection(CollectionMediator<Person>* mediator, const QString& path = QString(), FallbackPersonCollection* parent = nullptr);
    virtual ~FallbackPersonCollection();
 
    virtual bool load  () override;
diff --git a/src/person.h b/src/person.h
index 834caa587987765ac1b3b529c193219bcdb94263..ed78d3706a19fea3ce8539553c64680dea3a1f8d 100644
--- a/src/person.h
+++ b/src/person.h
@@ -100,7 +100,7 @@ public:
    //Mutator
    Q_INVOKABLE void addAddress(Address* addr);
    Q_INVOKABLE void addCustomField(const QString& key, const QString& value);
-   Q_INVOKABLE const QByteArray toVCard(QList<Account*> accounts) const;
+   Q_INVOKABLE const QByteArray toVCard(QList<Account*> accounts = {}) const;
 
 protected:
    //The D-Pointer can be shared if a PlaceHolderPerson is merged with a real one
diff --git a/src/personmodel.cpp b/src/personmodel.cpp
index 51b1f7717f913bc073682bc943814c461f2d7bbb..c9cc40c29cc978c4fa3bebd79ba95e55643f8de0 100644
--- a/src/personmodel.cpp
+++ b/src/personmodel.cpp
@@ -302,13 +302,15 @@ bool PersonModel::addItemCallback(const Person* c)
          d_ptr->m_hPlaceholders[c->uid()] = nullptr;
       }
    }
+
    return true;
 }
 
 bool PersonModel::removeItemCallback(const Person* item)
 {
-   Q_UNUSED(item)
-   return false;
+   if (item)
+      emit const_cast<Person*>(item)->changed();
+   return item;
 }
 
 bool PersonModel::addPerson(Person* c)
@@ -320,19 +322,22 @@ bool PersonModel::addPerson(Person* c)
    return true;
 }
 
-void PersonModel::disablePerson(Person* c)
-{
-   Q_UNUSED(c) //TODO re-implement
-//    if (c)
-//       c->setActive(false);
-}
-
 bool PersonModel::addNewPerson(Person* c, CollectionInterface* backend)
 {
-   if ((!backend) || (!collections().size()))
+   if ((!backend) && (!collections().size()))
       return false;
 
-   return (backend?backend:collections()[0])->editor<Person>()->addNew(c);
+   bool ret = false;
+
+   if (backend) {
+      ret |= backend->editor<Person>()->addNew(c);
+   }
+   else for (CollectionInterface* col :collections(CollectionInterface::SupportedFeatures::ADD)) {
+      if (col->id() != "trcb") //Do not add to the transitional contact backend
+         ret |= col->editor<Person>()->addNew(c);
+   }
+
+   return ret;
 }
 
 
diff --git a/src/personmodel.h b/src/personmodel.h
index c47ef9ba5aefffcf45929124bf3c195403834926..101a2a0f5e28fa9452d8960d47c9d9bb75e30496 100644
--- a/src/personmodel.h
+++ b/src/personmodel.h
@@ -56,7 +56,6 @@ public:
 
    //Mutator
    bool addPerson(Person* c);
-   void disablePerson(Person* c);
 
    //Getters
    Person* getPersonByUid   ( const QByteArray& uid );
diff --git a/src/private/collectionmodel_p.h b/src/private/collectionmodel_p.h
index 4278746ea9184357c997ebc39d9d49cdd180e8ef..f0fc85f6a40eca3fd1cfc1db4c2d249b9e325bb4 100644
--- a/src/private/collectionmodel_p.h
+++ b/src/private/collectionmodel_p.h
@@ -43,7 +43,7 @@ public:
     * index have to be implemented.
     */
    struct ProxyItem {
-      ProxyItem() : parent(nullptr),col(1),row(0),collection(nullptr),manageableCount(0){}
+      ProxyItem() : parent(nullptr),col(0),row(0),collection(nullptr),manageableCount(0){}
       int row;
       int col;
       CollectionInterface* collection;
diff --git a/src/private/videodevice_p.h b/src/private/videodevice_p.h
index 8f8f4019a6687747e203af51d2a5f72ddea0bd59..1fb46a00cb35a2cf9cccf5164a7e9b148a927cef 100644
--- a/src/private/videodevice_p.h
+++ b/src/private/videodevice_p.h
@@ -18,8 +18,17 @@
 #ifndef VIDEODEVICEPRIVATE_H
 #define VIDEODEVICEPRIVATE_H
 
-class VideoDevicePrivate
+#include <QtCore/QObject>
+#include <QtCore/QList>
+
+namespace Video {
+   class Channel;
+   class Device;
+}
+
+class VideoDevicePrivate : public QObject
 {
+   Q_OBJECT
 public:
    class PreferenceNames {
    public:
@@ -29,12 +38,18 @@ public:
       constexpr static const char* SIZE    = "size"   ;
    };
 
-   explicit VideoDevicePrivate();
+   explicit VideoDevicePrivate(Video::Device* parent = nullptr);
 
    //Attributes
    QString                m_DeviceId       ;
    Video::Channel*        m_pCurrentChannel;
    QList<Video::Channel*> m_lChannels      ;
+   bool                   m_RequireSave    ;
+
+   Video::Device* q_ptr;
+
+public Q_SLOTS:
+   void saveIdle();
 };
 
 #endif
diff --git a/src/private/videorenderermanager.cpp b/src/private/videorenderermanager.cpp
index f919dd19c50e85cca812b2ae0a07b3f52deb3818..aea07e358ea5ea85c6564e96c2a7e5e329df99aa 100644
--- a/src/private/videorenderermanager.cpp
+++ b/src/private/videorenderermanager.cpp
@@ -101,6 +101,12 @@ VideoRendererManager* VideoRendererManager::instance()
    return m_spInstance;
 }
 
+
+int VideoRendererManager::size() const
+{
+   return d_ptr->m_hRenderers.size();
+}
+
 ///Return the call Renderer or nullptr
 Video::Renderer* VideoRendererManager::getRenderer(const Call* call) const
 {
@@ -169,7 +175,7 @@ void VideoRendererManager::startPreview()
 }
 
 ///Is the video model fetching preview from a camera
-bool VideoRendererManager::isPreviewing()
+bool VideoRendererManager::isPreviewing() const
 {
    return d_ptr->m_PreviewState;
 }
@@ -270,11 +276,15 @@ void VideoRendererManagerPrivate::stoppedDecoding(const QString& id, const QStri
    Q_UNUSED(shmPath)
    Video::Renderer* r = m_hRenderers[id.toLatin1()];
 
-   if ( r )
-      r->stopRendering();
+   //Quit if for some reasons the renderer is not found
+   if ( !r ) {
+      qWarning() << "Cannot stop renrering, renderer" << id << "not found";
+      return;
+   }
+
+   r->stopRendering();
 
    qDebug() << "Video stopped for call" << id <<  "Renderer found:" << (m_hRenderers[id.toLatin1()] != nullptr);
-//    emit videoStopped();
 
    Video::Device* dev = Video::DeviceModel::instance()->getDevice(id);
 
@@ -292,8 +302,11 @@ void VideoRendererManagerPrivate::stoppedDecoding(const QString& id, const QStri
    QThread* t = m_hThreads[r];
    m_hThreads[r] = nullptr;
 
-   t->quit();
-   t->wait();
+   if (t) {
+      t->quit();
+      t->wait();
+   }
+
    delete r;
 
    t->deleteLater();
diff --git a/src/private/videorenderermanager.h b/src/private/videorenderermanager.h
index 1e5e637676729f59706f1ae9427aa57cf5d5417d..9e0edb325a7d0bac176ea65ef86aa971160f16d4 100644
--- a/src/private/videorenderermanager.h
+++ b/src/private/videorenderermanager.h
@@ -47,8 +47,9 @@ public:
    static VideoRendererManager* instance();
 
    //Getters
-   bool             isPreviewing   ();
-   Video::Renderer* previewRenderer();
+   bool             isPreviewing   () const;
+   Video::Renderer* previewRenderer()      ;
+   int              size           () const;
 
    //Helpers
    Video::Renderer* getRenderer(const Call* call) const;
diff --git a/src/useractionmodel.cpp b/src/useractionmodel.cpp
index e1fa7d46bd2470830e10aa2bd165f493f8198e5d..6dab5a9c727ef7be562b7bebc6955078e24fe4c5 100644
--- a/src/useractionmodel.cpp
+++ b/src/useractionmodel.cpp
@@ -76,7 +76,7 @@ public:
    SelectionState                                                    m_SelectionState     ;
    TypedStateMachine< bool, UserActionModel::Action>                 m_CurrentActions     ;
    TypedStateMachine< Qt::CheckState, UserActionModel::Action>       m_CurrentActionsState;
-   static const TypedStateMachine< QString, UserActionModel::Action> m_ActionNames        ;
+   TypedStateMachine< QString, UserActionModel::Action>              m_ActionNames        ;
    ActiveUserActionModel*                                            m_pActiveModel       ;
 
    //The mute per call, per media is not merged upstream yet, faking it for now
@@ -213,23 +213,23 @@ const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLe
 }};
 #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"            ),
-
-   /* JOIN            */ QObject::tr("ADD_NEW"         ),
-}};
-
 UserActionModelPrivate::UserActionModelPrivate(UserActionModel* parent) : QObject(parent),q_ptr(parent),
 m_pCall(nullptr), m_pActiveModel(nullptr)
 {
+   //Init the default names
+   m_ActionNames = {{
+      /* ACCEPT          */ QObject::tr("Accept"          ),
+      /* 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"            ),
+
+      /* JOIN            */ QObject::tr("Add new"         ),
+   }};
 }
 
 /**
@@ -289,7 +289,7 @@ QVariant UserActionModel::data(const QModelIndex& idx, int role ) const
 
    switch(role) {
       case Qt::DisplayRole:
-         return UserActionModelPrivate::m_ActionNames[action];
+         return d_ptr->m_ActionNames[action];
       case Qt::CheckStateRole:
          if (d_ptr->actionStatefulness[action][d_ptr->m_SelectionState] != UserActionModel::ActionStatfulnessLevel::UNISTATE)
             return d_ptr->m_CurrentActionsState[action];
@@ -378,6 +378,61 @@ void UserActionModelPrivate::updateCheckMask(int& ret, UserActionModel::Action a
       case UserActionModel::Action::COUNT__:
          break;
    };
+
+   //Avoid the noise
+   #pragma GCC diagnostic push
+   #pragma GCC diagnostic ignored "-Wswitch-enum"
+   //Update the labels
+   switch (action) {
+      case UserActionModel::Action::ACCEPT          :
+         switch(c->state()) {
+            case Call::State::DIALING        :
+               m_ActionNames[UserActionModel::Action::ACCEPT] = QObject::tr("Call");
+               break;
+            default:
+               m_ActionNames[UserActionModel::Action::ACCEPT] = QObject::tr("Accept");
+               break;
+         }
+         break;
+      case UserActionModel::Action::HOLD            :
+         switch(c->state()) {
+            case Call::State::HOLD           :
+            case Call::State::CONFERENCE_HOLD:
+            case Call::State::TRANSF_HOLD    :
+               m_ActionNames[UserActionModel::Action::HOLD] = QObject::tr("Unhold");
+               break;
+            default:
+               m_ActionNames[UserActionModel::Action::HOLD] = QObject::tr("Hold");
+               break;
+         }
+         break;
+      case UserActionModel::Action::HANGUP          :
+         switch(c->state()) {
+            case Call::State::DIALING        :
+               m_ActionNames[UserActionModel::Action::HANGUP] = QObject::tr("Cancel");
+               break;
+            case Call::State::FAILURE        :
+            case Call::State::ERROR          :
+            case Call::State::COUNT__        :
+            case Call::State::INITIALIZATION :
+            case Call::State::BUSY           :
+               m_ActionNames[UserActionModel::Action::HANGUP] = QObject::tr("Remove");
+               break;
+            default:
+               m_ActionNames[UserActionModel::Action::HANGUP] = QObject::tr("Hangup");
+               break;
+         }
+         break;
+      case UserActionModel::Action::JOIN            :
+      case UserActionModel::Action::ADD_NEW         :
+      case UserActionModel::Action::COUNT__         :
+      case UserActionModel::Action::MUTE_AUDIO      :
+      case UserActionModel::Action::MUTE_VIDEO      :
+      case UserActionModel::Action::SERVER_TRANSFER :
+      case UserActionModel::Action::RECORD          :
+         break;
+   }
+   #pragma GCC diagnostic pop
 }
 
 bool UserActionModelPrivate::updateByCall(UserActionModel::Action action, const Call* c)
diff --git a/src/vcardutils.cpp b/src/vcardutils.cpp
index 7e057e1c70198f2ca56df1d52feec34870331894..969e93218ea90e725afb9529a824aead8bd64aa5 100644
--- a/src/vcardutils.cpp
+++ b/src/vcardutils.cpp
@@ -54,7 +54,7 @@
 
 struct VCardMapper;
 
-typedef void (VCardMapper:: *mapToProperty)(Person*, const QByteArray&);
+typedef void (VCardMapper:: *mapToProperty)(Person*, const QString&, const QByteArray&);
 
 struct VCardMapper {
 
@@ -66,31 +66,34 @@ struct VCardMapper {
       m_hHash[VCardUtils::Property::FORMATTED_NAME] = &VCardMapper::setFormattedName;
       m_hHash[VCardUtils::Property::EMAIL] = &VCardMapper::setEmail;
       m_hHash[VCardUtils::Property::ORGANIZATION] = &VCardMapper::setOrganization;
+      m_hHash[VCardUtils::Property::TELEPHONE] = &VCardMapper::addContactMethod;
+      m_hHash[VCardUtils::Property::ADDRESS] = &VCardMapper::addAddress;
+      m_hHash[VCardUtils::Property::PHOTO] = &VCardMapper::setPhoto;
    }
 
-   void setFormattedName(Person* c, const QByteArray& fn) {
+   void setFormattedName(Person* c,  const QString&, const QByteArray& fn) {
       c->setFormattedName(QString::fromUtf8(fn));
    }
 
-   void setNames(Person* c, const QByteArray& fn) {
+   void setNames(Person* c,  const QString&, const QByteArray& fn) {
       QList<QByteArray> splitted = fn.split(';');
       c->setFamilyName(splitted[0].trimmed());
       c->setFirstName(splitted[1].trimmed());
    }
 
-   void setUid(Person* c, const QByteArray& fn) {
+   void setUid(Person* c,  const QString&, const QByteArray& fn) {
       c->setUid(fn);
    }
 
-   void setEmail(Person* c, const QByteArray& fn) {
+   void setEmail(Person* c,  const QString&, const QByteArray& fn) {
       c->setPreferredEmail(fn);
    }
 
-   void setOrganization(Person* c, const QByteArray& fn) {
+   void setOrganization(Person* c, const QString&,  const QByteArray& fn) {
       c->setOrganization(QString::fromUtf8(fn));
    }
 
-   void setPhoto(Person* c, const QByteArray& fn, const QByteArray& key) {
+   void setPhoto(Person* c, const QString& key, const QByteArray& fn) {
       QByteArray type = "PNG";
 
       QRegExp rx("TYPE=([A-Za-z]*)");
@@ -139,10 +142,11 @@ struct VCardMapper {
    }
 
    bool metacall(Person* c, const QByteArray& key, const QByteArray& value) {
-      if (!m_hHash[key]) {
+      const QStringList settings = QString(key).split(';');
+      if (!m_hHash[settings[0].toLatin1()]) {
          if(key.contains(VCardUtils::Property::PHOTO)) {
             //key must contain additionnal attributes, we don't need them right now (ENCODING, TYPE...)
-            setPhoto(c, value, key);
+            setPhoto(c, key, value);
             return true;
          }
 
@@ -158,7 +162,7 @@ struct VCardMapper {
 
          return false;
       }
-      (this->*(m_hHash[key]))(c,value);
+      (this->*(m_hHash[settings[0].toLatin1()]))(c,key,value);
       return true;
    }
 };
@@ -244,7 +248,7 @@ const QByteArray VCardUtils::endVCard()
    return result.toUtf8();
 }
 
-QList< Person* > VCardUtils::loadDir (const QUrl& path, bool& ok)
+QList< Person* > VCardUtils::loadDir (const QUrl& path, bool& ok, QHash<const Person*,QString>& paths)
 {
    QList< Person* > ret;
 
@@ -257,6 +261,7 @@ QList< Person* > VCardUtils::loadDir (const QUrl& path, bool& ok)
          Person* p = new Person();
          mapToPerson(p,QUrl(dir.absoluteFilePath(file)));
          ret << p;
+         paths[p] = dir.absoluteFilePath(file);
       }
    }
 
diff --git a/src/vcardutils.h b/src/vcardutils.h
index ff64f662821dc741f1fdef6d7717e179e7ddd17c..6228bdef7bc9182a90b2b60060a375d70c193f69 100644
--- a/src/vcardutils.h
+++ b/src/vcardutils.h
@@ -79,7 +79,7 @@ public:
    const QByteArray endVCard();
 
    //Loading
-   static QList<Person*> loadDir(const QUrl& path, bool& ok);
+   static QList<Person*> loadDir(const QUrl& path, bool& ok, QHash<const Person*, QString>& paths);
 
    //Mapping
    static bool mapToPerson(Person* p, const QUrl& url, Account** a = nullptr);
diff --git a/src/video/channel.cpp b/src/video/channel.cpp
index beb9b3b062586beb66a7e8001fd40acbdfa8d876..f818d0e71ca81d81c08e005fc33ff48c0171eb82 100644
--- a/src/video/channel.cpp
+++ b/src/video/channel.cpp
@@ -43,7 +43,7 @@ Video::Channel::~Channel()
 
 QVariant Video::Channel::data( const QModelIndex& index, int role) const
 {
-   if (index.isValid() && role == Qt::DisplayRole) {
+   if (index.isValid() && role == Qt::DisplayRole && d_ptr->m_lValidResolutions.size() > index.row()) {
       return d_ptr->m_lValidResolutions[index.row()]->name();
    }
    return QVariant();
@@ -84,6 +84,10 @@ bool Video::Channel::setActiveResolution(Video::Resolution* res) {
       qWarning() << "Invalid active resolution" << (res?res->name():"NULL");
       return false;
    }
+
+   if (d_ptr->m_pCurrentResolution == res)
+      return false;
+
    d_ptr->m_pCurrentResolution = res;
    d_ptr->m_pDevice->save();
    return true;
diff --git a/src/video/configurationproxy.cpp b/src/video/configurationproxy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b55acbab20526388cc3289901bfce90c240ae081
--- /dev/null
+++ b/src/video/configurationproxy.cpp
@@ -0,0 +1,291 @@
+/****************************************************************************
+ *   Copyright (C) 2015 by Savoir-Faire Linux                               *
+ *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
+ *                                                                          *
+ *   This library is free software; you can redistribute it and/or          *
+ *   modify it under the terms of the GNU Lesser General Public             *
+ *   License as published by the Free Software Foundation; either           *
+ *   version 2.1 of the License, or (at your option) any later version.     *
+ *                                                                          *
+ *   This library is distributed in the hope that it will be useful,        *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      *
+ *   Lesser General Public License for more details.                        *
+ *                                                                          *
+ *   You should have received a copy of the GNU General Public License      *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+#include "configurationproxy.h"
+
+//Qt
+#include <QtCore/QIdentityProxyModel>
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QItemSelectionModel>
+
+//Ring
+#include <video/sourcemodel.h>
+#include <video/devicemodel.h>
+#include <video/channel.h>
+#include <video/resolution.h>
+#include <video/rate.h>
+
+namespace ConfigurationProxyPrivate {
+   static QIdentityProxyModel* m_spDeviceModel    = nullptr;
+   static QIdentityProxyModel* m_spChannelModel   = nullptr;
+   static QIdentityProxyModel* m_spResolutionModel= nullptr;
+   static QIdentityProxyModel* m_spRateModel      = nullptr;
+
+   static QItemSelectionModel* m_spDeviceSelectionModel    = nullptr;
+   static QItemSelectionModel* m_spChannelSelectionModel   = nullptr;
+   static QItemSelectionModel* m_spResolutionSelectionModel= nullptr;
+   static QItemSelectionModel* m_spRateSelectionModel      = nullptr;
+
+   //Helper
+   static Video::Device*     currentDevice    ();
+   static Video::Channel*    currentChannel   ();
+   static Video::Resolution* currentResolution();
+//    static Video::Rate*       currentRate      ();
+
+   static void changeDevice    ();
+   static void changeChannel   ();
+   static void changeResolution();
+   static void changeRate      ();
+
+   void updateDeviceSelection    ();
+   void updateChannelSelection   ();
+   void updateResolutionSelection();
+   void updateRateSelection      ();
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::deviceModel()
+{
+   if (!ConfigurationProxyPrivate::m_spDeviceModel) {
+      ConfigurationProxyPrivate::m_spDeviceModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      ConfigurationProxyPrivate::m_spDeviceModel->setSourceModel(Video::DeviceModel::instance());
+      ConfigurationProxyPrivate::updateDeviceSelection();
+   }
+   return ConfigurationProxyPrivate::m_spDeviceModel;
+}
+
+static Video::Device* ConfigurationProxyPrivate::currentDevice()
+{
+   return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice();
+}
+
+static Video::Channel* ConfigurationProxyPrivate::currentChannel()
+{
+   if (Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel())
+      return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel();
+
+   return nullptr;
+}
+
+static Video::Resolution* ConfigurationProxyPrivate::currentResolution()
+{
+   if (Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution()
+   )
+      return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution();
+   return nullptr;
+}
+
+/*static Video::Rate* ConfigurationProxyPrivate::currentRate()
+{
+   if (Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()
+    && Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution()
+   )
+      return Video::DeviceModel::instance()->Video::DeviceModel::instance()->activeDevice()->activeChannel()->activeResolution()->activeRate();
+
+   return nullptr;
+}*/
+
+void ConfigurationProxyPrivate::changeDevice()
+{
+   Video::ConfigurationProxy::deviceSelectionModel();
+
+   Video::DeviceModel::instance()->setActive(ConfigurationProxyPrivate::m_spDeviceSelectionModel->currentIndex());
+
+   ((QIdentityProxyModel*) Video::ConfigurationProxy::channelModel())->setSourceModel(ConfigurationProxyPrivate::currentDevice());
+   changeChannel();
+}
+
+void ConfigurationProxyPrivate::changeChannel()
+{
+   Video::ConfigurationProxy::channelSelectionModel();
+
+   Video::Device* dev = ConfigurationProxyPrivate::currentDevice();
+
+   if (dev)
+      dev->setActiveChannel(ConfigurationProxyPrivate::m_spChannelSelectionModel->currentIndex().row());
+
+   ((QIdentityProxyModel*) Video::ConfigurationProxy::resolutionModel())->setSourceModel(ConfigurationProxyPrivate::currentChannel());
+
+   updateChannelSelection();
+
+   changeResolution();
+}
+
+void ConfigurationProxyPrivate::changeResolution()
+{
+   Video::ConfigurationProxy::resolutionSelectionModel();
+
+   Video::Channel* chan = ConfigurationProxyPrivate::currentChannel();
+
+   if (chan)
+      chan->setActiveResolution(ConfigurationProxyPrivate::m_spResolutionSelectionModel->currentIndex().row());
+
+   ((QIdentityProxyModel*) Video::ConfigurationProxy::rateModel())->setSourceModel(ConfigurationProxyPrivate::currentResolution());
+
+   updateResolutionSelection();
+
+   changeRate();
+}
+
+void ConfigurationProxyPrivate::changeRate()
+{
+   Video::ConfigurationProxy::rateSelectionModel();
+
+   Video::Resolution* res = ConfigurationProxyPrivate::currentResolution();
+
+   if (res)
+      res->setActiveRate(ConfigurationProxyPrivate::m_spRateSelectionModel->currentIndex().row());
+
+   updateRateSelection();
+}
+
+void ConfigurationProxyPrivate::updateDeviceSelection()
+{
+   if (ConfigurationProxyPrivate::m_spDeviceModel) {
+      const QModelIndex& idx = ConfigurationProxyPrivate::m_spDeviceModel->index(Video::DeviceModel::instance()->activeIndex(),0);
+      if (idx.row() != Video::ConfigurationProxy::deviceSelectionModel()->currentIndex().row())
+         Video::ConfigurationProxy::deviceSelectionModel()->setCurrentIndex(idx , QItemSelectionModel::ClearAndSelect);
+   }
+}
+
+void ConfigurationProxyPrivate::updateChannelSelection()
+{
+   Video::Device* dev = ConfigurationProxyPrivate::currentDevice();
+   if (dev) {
+      Video::Channel* chan = dev->activeChannel();
+      if (chan) {
+         const QModelIndex& newIdx = dev->index(chan->relativeIndex(),0);
+         if (newIdx.row() != Video::ConfigurationProxy::channelSelectionModel()->currentIndex().row())
+            Video::ConfigurationProxy::channelSelectionModel()->setCurrentIndex(newIdx, QItemSelectionModel::ClearAndSelect );
+      }
+   }
+}
+
+void ConfigurationProxyPrivate::updateResolutionSelection()
+{
+   Video::Channel* chan = ConfigurationProxyPrivate::currentChannel();
+   if (chan) {
+      Video::Resolution* res = chan->activeResolution();
+      if (res) {
+         const QModelIndex& newIdx = chan->index(res->relativeIndex(),0);
+         if (newIdx.row() != Video::ConfigurationProxy::resolutionSelectionModel()->currentIndex().row())
+            Video::ConfigurationProxy::resolutionSelectionModel()->setCurrentIndex(newIdx, QItemSelectionModel::ClearAndSelect);
+      }
+   }
+}
+
+void ConfigurationProxyPrivate::updateRateSelection()
+{
+   Video::Resolution* res = ConfigurationProxyPrivate::currentResolution();
+   if (res) {
+      Video::Rate* rate = res->activeRate();
+      if (rate) {
+         const QModelIndex& newIdx = res->index(rate->relativeIndex(),0);
+         if (newIdx.row() != Video::ConfigurationProxy::rateSelectionModel()->currentIndex().row())
+            Video::ConfigurationProxy::rateSelectionModel()->setCurrentIndex(newIdx, QItemSelectionModel::ClearAndSelect);
+      }
+   }
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::channelModel()
+{
+   if (!ConfigurationProxyPrivate::m_spChannelModel) {
+      ConfigurationProxyPrivate::m_spChannelModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      Video::Device* dev = ConfigurationProxyPrivate::currentDevice();
+      if (dev) {
+         ConfigurationProxyPrivate::m_spChannelModel->setSourceModel(dev);
+      }
+   }
+   return ConfigurationProxyPrivate::m_spChannelModel;
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::resolutionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spResolutionModel) {
+      ConfigurationProxyPrivate::m_spResolutionModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      Video::Channel* chan = ConfigurationProxyPrivate::currentChannel();
+      if (chan) {
+         ConfigurationProxyPrivate::m_spResolutionModel->setSourceModel(chan);
+      }
+   }
+   return ConfigurationProxyPrivate::m_spResolutionModel;
+}
+
+QAbstractItemModel* Video::ConfigurationProxy::rateModel()
+{
+   if (!ConfigurationProxyPrivate::m_spRateModel) {
+      ConfigurationProxyPrivate::m_spRateModel = new QIdentityProxyModel(Video::SourceModel::instance());
+      ConfigurationProxyPrivate::m_spRateModel->setSourceModel(ConfigurationProxyPrivate::currentResolution());
+   }
+   return ConfigurationProxyPrivate::m_spRateModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::deviceSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spDeviceSelectionModel) {
+      ConfigurationProxyPrivate::m_spDeviceSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spDeviceModel);
+
+      ConfigurationProxyPrivate::updateDeviceSelection();
+
+      //Can happen if a device is removed
+      QObject::connect(Video::DeviceModel::instance(), &Video::DeviceModel::currentIndexChanged,[](int idx) {
+         ConfigurationProxyPrivate::m_spDeviceSelectionModel->setCurrentIndex(ConfigurationProxyPrivate::m_spDeviceModel->index(idx,0), QItemSelectionModel::ClearAndSelect );
+      });
+
+      QObject::connect(ConfigurationProxyPrivate::m_spDeviceSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeDevice);
+   }
+   return ConfigurationProxyPrivate::m_spDeviceSelectionModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::channelSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spChannelSelectionModel) {
+      ConfigurationProxyPrivate::m_spChannelSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spChannelModel);
+
+      ConfigurationProxyPrivate::updateChannelSelection();
+
+      QObject::connect(ConfigurationProxyPrivate::m_spChannelSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeChannel);
+   }
+   return ConfigurationProxyPrivate::m_spChannelSelectionModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::resolutionSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spResolutionSelectionModel) {
+      ConfigurationProxyPrivate::m_spResolutionSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spResolutionModel);
+
+      ConfigurationProxyPrivate::updateResolutionSelection();
+
+      QObject::connect(ConfigurationProxyPrivate::m_spResolutionSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeResolution);
+   }
+   return ConfigurationProxyPrivate::m_spResolutionSelectionModel;
+}
+
+QItemSelectionModel* Video::ConfigurationProxy::rateSelectionModel()
+{
+   if (!ConfigurationProxyPrivate::m_spRateSelectionModel) {
+      ConfigurationProxyPrivate::m_spRateSelectionModel = new QItemSelectionModel(ConfigurationProxyPrivate::m_spRateModel);
+
+      ConfigurationProxyPrivate::updateRateSelection();
+
+      QObject::connect(ConfigurationProxyPrivate::m_spRateSelectionModel,&QItemSelectionModel::currentChanged, &ConfigurationProxyPrivate::changeRate);
+   }
+   return ConfigurationProxyPrivate::m_spRateSelectionModel;
+}
diff --git a/src/video/configurationproxy.h b/src/video/configurationproxy.h
new file mode 100644
index 0000000000000000000000000000000000000000..93985684b19423ffbedbb1f6fc6853b92b6f0076
--- /dev/null
+++ b/src/video/configurationproxy.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+ *   Copyright (C) 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 CONFIGURATIONPROXY_H
+#define CONFIGURATIONPROXY_H
+
+
+class QAbstractItemModel;
+class QItemSelectionModel;
+
+namespace Video {
+
+/**
+ * This class is used to simplify the configuration process.
+ * Currently, every devices have their own model tree. This
+ * proxy flatten the three to the clients don't have to
+ * implement the managing logic.
+ */
+class ConfigurationProxy {
+public:
+   static QAbstractItemModel* deviceModel    ();
+   static QAbstractItemModel* channelModel   ();
+   static QAbstractItemModel* resolutionModel();
+   static QAbstractItemModel* rateModel      ();
+
+   static QItemSelectionModel* deviceSelectionModel    ();
+   static QItemSelectionModel* channelSelectionModel   ();
+   static QItemSelectionModel* resolutionSelectionModel();
+   static QItemSelectionModel* rateSelectionModel      ();
+};
+
+} //namespace Video
+
+#endif
diff --git a/src/video/device.cpp b/src/video/device.cpp
index 6ac6c65048579e1517ec1c415c0e9c32ce28ed89..500ceccdeaa81a7b86f09da48c65f8728e1db8f8 100644
--- a/src/video/device.cpp
+++ b/src/video/device.cpp
@@ -17,6 +17,9 @@
  ***************************************************************************/
 #include "device.h"
 
+//Qt
+#include <QtCore/QTimer>
+
 //Ring
 #include "../dbus/videomanager.h"
 #include "devicemodel.h"
@@ -24,20 +27,22 @@
 #include "rate.h"
 #include "channel.h"
 #include "renderer.h"
+#include "previewmanager.h"
 
 //Ring private
 #include "../private/videochannel_p.h"
 #include "../private/videodevice_p.h"
 #include "../private/videorate_p.h"
 #include "../private/videoresolution_p.h"
+#include "../private/videorenderermanager.h"
 
-VideoDevicePrivate::VideoDevicePrivate() : m_pCurrentChannel(nullptr)
+VideoDevicePrivate::VideoDevicePrivate(Video::Device* parent) : QObject(parent),m_pCurrentChannel(nullptr),m_RequireSave(false),q_ptr(parent)
 {
 }
 
 ///Constructor
 Video::Device::Device(const QString &id) : QAbstractListModel(nullptr),
-d_ptr(new VideoDevicePrivate())
+d_ptr(new VideoDevicePrivate(this))
 {
    d_ptr->m_DeviceId = id;
    VideoManagerInterface& interface = DBus::VideoManager::instance();
@@ -73,7 +78,7 @@ Video::Device::~Device()
 
 QVariant Video::Device::data( const QModelIndex& index, int role) const
 {
-   if (index.isValid() && role == Qt::DisplayRole) {
+   if (index.isValid() && role == Qt::DisplayRole && d_ptr->m_lChannels.size() > index.row()) {
       return d_ptr->m_lChannels[index.row()]->name();
    }
    return QVariant();
@@ -112,13 +117,11 @@ QList<Video::Channel*> Video::Device::channelList() const
 ///Save the current settings
 void Video::Device::save()
 {
-   //In case new (unsupported) fields are added, merge with existing
-   VideoManagerInterface& interface = DBus::VideoManager::instance();
-   MapStringString pref = interface.getSettings(d_ptr->m_DeviceId);
-   pref[VideoDevicePrivate::PreferenceNames::CHANNEL] = activeChannel()->name();
-   pref[VideoDevicePrivate::PreferenceNames::SIZE   ] = activeChannel()->activeResolution()->name();
-   pref[VideoDevicePrivate::PreferenceNames::RATE   ] = activeChannel()->activeResolution()->activeRate()->name();
-   interface.applySettings(d_ptr->m_DeviceId,pref);
+   if (!d_ptr->m_RequireSave) {
+      d_ptr->m_RequireSave = true;
+      //A little delay wont hurt
+      QTimer::singleShot(100,d_ptr.data(),SLOT(saveIdle()));
+   }
 }
 
 ///Get the device id
@@ -142,10 +145,14 @@ bool Video::Device::isActive() const
 
 bool Video::Device::setActiveChannel(Video::Channel* chan)
 {
-   if (!chan || !d_ptr->m_lChannels.indexOf(chan)) {
+   if ((!chan) || (d_ptr->m_lChannels.indexOf(chan) == -1)) {
       qWarning() << "Trying to set an invalid channel" << (chan?chan->name():"NULL") << "for" << id();
       return false;
    }
+
+   if (d_ptr->m_pCurrentChannel == chan)
+      return false;
+
    d_ptr->m_pCurrentChannel = chan;
    save();
    return true;
@@ -174,3 +181,46 @@ Video::Channel* Video::Device::activeChannel() const
    }
    return d_ptr->m_pCurrentChannel;
 }
+
+
+void VideoDevicePrivate::saveIdle()
+{
+   m_RequireSave = false;
+
+   //In case new (unsupported) fields are added, merge with existing
+   VideoManagerInterface& interface = DBus::VideoManager::instance();
+   MapStringString pref = interface.getSettings(m_DeviceId);
+
+   Video::Channel* chan = q_ptr->activeChannel();
+
+   if (!chan) {
+      qWarning() << "Saving video failed: Invalid channel";
+      return;
+   }
+
+   Video::Resolution* res = chan->activeResolution();
+
+   if (!res) {
+      qWarning() << "Saving video failed: Invalid resolution";
+      return;
+   }
+
+   Video::Rate* rate = res->activeRate();
+
+   if (!rate) {
+      qWarning() << "Saving video failed: Invalid rate";
+      return;
+   }
+
+   pref[VideoDevicePrivate::PreferenceNames::CHANNEL] = chan->name ();
+   pref[VideoDevicePrivate::PreferenceNames::SIZE   ] = res ->name ();
+   pref[VideoDevicePrivate::PreferenceNames::RATE   ] = rate->name ();
+   interface.applySettings(m_DeviceId,pref);
+
+   //If the preview is running, reload it
+   //doing this during a call will cause re-invite, this is unwanted
+   if (Video::PreviewManager::instance()->isPreviewing() && VideoRendererManager::instance()->size() == 1) {
+      Video::PreviewManager::instance()->stopPreview();
+      Video::PreviewManager::instance()->startPreview();
+   }
+}
diff --git a/src/video/devicemodel.cpp b/src/video/devicemodel.cpp
index db84231bae2d080c8178eeb0ecbba6d41a9a2607..e17c33672a13933723bd5ac3f1cf54337846ce35 100644
--- a/src/video/devicemodel.cpp
+++ b/src/video/devicemodel.cpp
@@ -16,13 +16,18 @@
  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
  ***************************************************************************/
 #include "devicemodel.h"
+
+//Qt
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTimer>
+
+//Ring
 #include "device.h"
 #include <call.h>
 #include <account.h>
+#include <video/previewmanager.h>
 #include "../dbus/videomanager.h"
-
-#include <QtCore/QCoreApplication>
-#include <QtCore/QTimer>
+#include "../private/videorenderermanager.h"
 
 Video::DeviceModel* Video::DeviceModel::m_spInstance = nullptr;
 
@@ -46,7 +51,6 @@ private Q_SLOTS:
 
 Video::DeviceModelPrivate::DeviceModelPrivate() : m_pDummyDevice(nullptr),m_pActiveDevice(nullptr)
 {
-
 }
 
 ///
@@ -61,6 +65,8 @@ d_ptr(new Video::DeviceModelPrivate())
 {
    m_spInstance = this;
    reload();
+   VideoManagerInterface& interface = DBus::VideoManager::instance();
+   connect(&interface, SIGNAL(deviceEvent()), this, SLOT(reload()));
 }
 
 Video::DeviceModel* Video::DeviceModel::instance()
@@ -84,7 +90,7 @@ QHash<int,QByteArray> Video::DeviceModel::roleNames() const
 ///Get data from the model
 QVariant Video::DeviceModel::data( const QModelIndex& idx, int role) const
 {
-   if(idx.column() == 0 && role == Qt::DisplayRole)
+   if(idx.isValid() && idx.column() == 0 && role == Qt::DisplayRole && d_ptr->m_lDevices.size() > idx.row())
       return QVariant(d_ptr->m_lDevices[idx.row()]->id());
    return QVariant();
 }
@@ -127,12 +133,18 @@ Video::DeviceModel::~DeviceModel()
 ///Save the current model over dbus
 void Video::DeviceModel::setActive(const QModelIndex& idx)
 {
-   if (idx.isValid()) {
+   if (idx.isValid() && d_ptr->m_lDevices.size() > idx.row()) {
       VideoManagerInterface& interface = DBus::VideoManager::instance();
       interface.setDefaultDevice(d_ptr->m_lDevices[idx.row()]->id());
       d_ptr->m_pActiveDevice = d_ptr->m_lDevices[idx.row()];
       emit changed();
       emit currentIndexChanged(idx.row());
+
+      //If the only renderer is the preview, reload it
+      if (Video::PreviewManager::instance()->isPreviewing() && VideoRendererManager::instance()->size() == 1) {
+         Video::PreviewManager::instance()->stopPreview();
+         Video::PreviewManager::instance()->startPreview();
+      }
    }
 }
 
@@ -173,12 +185,16 @@ void Video::DeviceModel::reload()
    }
    foreach(Video::Device* dev, d_ptr->m_hDevices) {
       if (dev && devicesHash.key(dev).isEmpty()) {
-         delete dev;
+         dev->deleteLater();
       }
    }
+   d_ptr->m_pActiveDevice = nullptr;
    d_ptr->m_hDevices.clear();
    d_ptr->m_hDevices = devicesHash;
+
+   beginResetModel();
    d_ptr->m_lDevices = d_ptr->m_hDevices.values();
+   endResetModel();
 
    emit layoutChanged();
 //    channelModel   ()->reload();
@@ -212,7 +228,6 @@ Video::Device* Video::DeviceModel::activeDevice() const
    return d_ptr->m_pActiveDevice;
 }
 
-
 int Video::DeviceModel::activeIndex() const
 {
    return d_ptr->m_lDevices.indexOf(activeDevice());
diff --git a/src/video/resolution.cpp b/src/video/resolution.cpp
index 42d41153c73d4fc3daddf20bb0d84bec407bd3bb..d5de19fa6e2dc2f076d7a8862153f3551251c009 100644
--- a/src/video/resolution.cpp
+++ b/src/video/resolution.cpp
@@ -50,7 +50,6 @@ const QString Video::Resolution::name() const
    return QString::number(width())+'x'+QString::number(height());
 }
 
-
 QVariant Video::Resolution::data( const QModelIndex& index, int role) const
 {
    if (index.isValid() && role == Qt::DisplayRole && index.row() < d_ptr->m_lValidRates.size()) {
@@ -79,23 +78,24 @@ bool Video::Resolution::setData( const QModelIndex& index, const QVariant &value
    return false;
 }
 
-
 const QList<Video::Rate*> Video::Resolution::validRates() const {
    return d_ptr->m_lValidRates;
 }
 
-
 bool Video::Resolution::setActiveRate(Video::Rate* rate) {
    if (!rate || (d_ptr->m_lValidRates.indexOf(rate) == -1)) {
       qWarning() << "Trying to set an invalid rate" << rate;
       return false;
    }
+
+   if (d_ptr->m_pCurrentRate == rate)
+      return false;
+
    d_ptr->m_pCurrentRate = rate;
    d_ptr->m_pChannel->device()->save();
    return true;
 }
 
-
 bool Video::Resolution::setActiveRate(int idx)
 {
    if (idx >= d_ptr->m_lValidRates.size() || idx < 0) return false;