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;