Commit d308d0a3 authored by Ronan Abhamon's avatar Ronan Abhamon

feat(ui/views/App/MainWindow/ContactEdit): supports the add of new contacts

parent 338fe8c0
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>options_over</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="options_over">
<path d="M20,40 C31.045695,40 40,31.045695 40,20 C40,8.954305 31.045695,0 20,0 C8.954305,0 0,8.954305 0,20 C0,31.045695 8.954305,40 20,40 Z" fill="#4B5964"></path>
<path d="M29.0873544,21.9006986 L29.0873544,18.0990443 L27.457167,18.0990443 C27.276035,17.3980628 26.9959394,16.7371962 26.6347091,16.1290447 L27.7891991,14.9803711 L25.0877236,12.2919042 L23.933492,13.441092 C23.3229145,13.0816012 22.6583335,12.8028543 21.9537018,12.6225946 L21.9537018,11 L18.133911,11 L18.133911,12.6225946 C17.4292793,12.8028543 16.765215,13.0816012 16.1538624,13.441092 L14.9993724,12.2919042 L12.298672,14.9803711 L13.453162,16.1290447 C13.0916733,16.7369391 12.8115777,17.3980628 12.6301874,18.0990443 L11,18.0990443 L11,21.9006986 L12.6301874,21.9006986 C12.8115777,22.6019372 13.0916733,23.2630609 13.4523869,23.8712124 L12.298672,25.0196289 L14.9998892,27.7078387 L16.1538624,26.5591651 C16.765215,26.9183988 17.4292793,27.1974028 18.133911,27.3774054 L18.133911,29 L21.9537018,29 L21.9537018,27.3774054 C22.6585919,27.1974028 23.3229145,26.9183988 23.9337503,26.5591651 L25.0877236,27.7078387 L27.7891991,25.0196289 L26.6347091,23.8712124 C26.9959394,23.2630609 27.2762934,22.6019372 27.457167,21.9006986 L29.0873544,21.9006986 L29.0873544,21.9006986 Z M15.9908179,20.0001286 C15.9908179,22.227789 17.8054963,24.0334719 20.0439356,24.0334719 C22.2818581,24.0334719 24.0965365,22.227789 24.0965365,20.0001286 C24.0965365,17.7729825 22.2818581,15.9667852 20.0439356,15.9667852 C17.8054963,15.9667852 15.9908179,17.7729825 15.9908179,20.0001286 L15.9908179,20.0001286 Z" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>options_default</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="options_default">
<path d="M20,40 C31.045695,40 40,31.045695 40,20 C40,8.954305 31.045695,0 20,0 C8.954305,0 0,8.954305 0,20 C0,31.045695 8.954305,40 20,40 Z" fill="#96A6B1"></path>
<path d="M29.0873544,21.9006986 L29.0873544,18.0990443 L27.457167,18.0990443 C27.276035,17.3980628 26.9959394,16.7371962 26.6347091,16.1290447 L27.7891991,14.9803711 L25.0877236,12.2919042 L23.933492,13.441092 C23.3229145,13.0816012 22.6583335,12.8028543 21.9537018,12.6225946 L21.9537018,11 L18.133911,11 L18.133911,12.6225946 C17.4292793,12.8028543 16.765215,13.0816012 16.1538624,13.441092 L14.9993724,12.2919042 L12.298672,14.9803711 L13.453162,16.1290447 C13.0916733,16.7369391 12.8115777,17.3980628 12.6301874,18.0990443 L11,18.0990443 L11,21.9006986 L12.6301874,21.9006986 C12.8115777,22.6019372 13.0916733,23.2630609 13.4523869,23.8712124 L12.298672,25.0196289 L14.9998892,27.7078387 L16.1538624,26.5591651 C16.765215,26.9183988 17.4292793,27.1974028 18.133911,27.3774054 L18.133911,29 L21.9537018,29 L21.9537018,27.3774054 C22.6585919,27.1974028 23.3229145,26.9183988 23.9337503,26.5591651 L25.0877236,27.7078387 L27.7891991,25.0196289 L26.6347091,23.8712124 C26.9959394,23.2630609 27.2762934,22.6019372 27.457167,21.9006986 L29.0873544,21.9006986 L29.0873544,21.9006986 Z M15.9908179,20.0001286 C15.9908179,22.227789 17.8054963,24.0334719 20.0439356,24.0334719 C22.2818581,24.0334719 24.0965365,22.227789 24.0965365,20.0001286 C24.0965365,17.7729825 22.2818581,15.9667852 20.0439356,15.9667852 C17.8054963,15.9667852 15.9908179,17.7729825 15.9908179,20.0001286 L15.9908179,20.0001286 Z" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>options_clic</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="options_clic">
<path d="M20,40 C31.045695,40 40,31.045695 40,20 C40,8.954305 31.045695,0 20,0 C8.954305,0 0,8.954305 0,20 C0,31.045695 8.954305,40 20,40 Z" fill="#FF5E00"></path>
<path d="M29.0873544,21.9006986 L29.0873544,18.0990443 L27.457167,18.0990443 C27.276035,17.3980628 26.9959394,16.7371962 26.6347091,16.1290447 L27.7891991,14.9803711 L25.0877236,12.2919042 L23.933492,13.441092 C23.3229145,13.0816012 22.6583335,12.8028543 21.9537018,12.6225946 L21.9537018,11 L18.133911,11 L18.133911,12.6225946 C17.4292793,12.8028543 16.765215,13.0816012 16.1538624,13.441092 L14.9993724,12.2919042 L12.298672,14.9803711 L13.453162,16.1290447 C13.0916733,16.7369391 12.8115777,17.3980628 12.6301874,18.0990443 L11,18.0990443 L11,21.9006986 L12.6301874,21.9006986 C12.8115777,22.6019372 13.0916733,23.2630609 13.4523869,23.8712124 L12.298672,25.0196289 L14.9998892,27.7078387 L16.1538624,26.5591651 C16.765215,26.9183988 17.4292793,27.1974028 18.133911,27.3774054 L18.133911,29 L21.9537018,29 L21.9537018,27.3774054 C22.6585919,27.1974028 23.3229145,26.9183988 23.9337503,26.5591651 L25.0877236,27.7078387 L27.7891991,25.0196289 L26.6347091,23.8712124 C26.9959394,23.2630609 27.2762934,22.6019372 27.457167,21.9006986 L29.0873544,21.9006986 L29.0873544,21.9006986 Z M15.9908179,20.0001286 C15.9908179,22.227789 17.8054963,24.0334719 20.0439356,24.0334719 C22.2818581,24.0334719 24.0965365,22.227789 24.0965365,20.0001286 C24.0965365,17.7729825 22.2818581,15.9667852 20.0439356,15.9667852 C17.8054963,15.9667852 15.9908179,17.7729825 15.9908179,20.0001286 L15.9908179,20.0001286 Z" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</g>
</svg>
\ No newline at end of file
...@@ -53,6 +53,9 @@ ...@@ -53,6 +53,9 @@
<file>assets/images/delete_hovered.svg</file> <file>assets/images/delete_hovered.svg</file>
<file>assets/images/delete_normal.svg</file> <file>assets/images/delete_normal.svg</file>
<file>assets/images/delete_pressed.svg</file> <file>assets/images/delete_pressed.svg</file>
<file>assets/images/edit_hovered.svg</file>
<file>assets/images/edit_normal.svg</file>
<file>assets/images/edit_pressed.svg</file>
<file>assets/images/ended_call.svg</file> <file>assets/images/ended_call.svg</file>
<file>assets/images/filter.svg</file> <file>assets/images/filter.svg</file>
<file>assets/images/fullscreen_hovered.svg</file> <file>assets/images/fullscreen_hovered.svg</file>
......
#include <QtDebug>
#include "../../app/App.hpp" #include "../../app/App.hpp"
#include "ContactModel.hpp" #include "ContactModel.hpp"
...@@ -16,6 +18,18 @@ ContactModel::ContactModel (shared_ptr<linphone::Friend> linphone_friend) { ...@@ -16,6 +18,18 @@ ContactModel::ContactModel (shared_ptr<linphone::Friend> linphone_friend) {
App::getInstance()->getEngine()->setObjectOwnership(m_vcard.get(), QQmlEngine::CppOwnership); App::getInstance()->getEngine()->setObjectOwnership(m_vcard.get(), QQmlEngine::CppOwnership);
} }
ContactModel::ContactModel (VcardModel *vcard) {
QQmlEngine *engine = App::getInstance()->getEngine();
if (engine->objectOwnership(vcard) == QQmlEngine::CppOwnership)
throw std::invalid_argument("A contact is already linked to this vcard.");
m_linphone_friend = linphone::Friend::newFromVcard(vcard->m_vcard);
m_linphone_friend->setData(NAME, *this);
m_vcard.reset(vcard);
engine->setObjectOwnership(vcard, QQmlEngine::CppOwnership);
}
Presence::PresenceStatus ContactModel::getPresenceStatus () const { Presence::PresenceStatus ContactModel::getPresenceStatus () const {
return Presence::PresenceStatus::Offline; return Presence::PresenceStatus::Offline;
} }
......
...@@ -22,6 +22,7 @@ class ContactModel : public QObject { ...@@ -22,6 +22,7 @@ class ContactModel : public QObject {
public: public:
ContactModel (std::shared_ptr<linphone::Friend> linphone_friend); ContactModel (std::shared_ptr<linphone::Friend> linphone_friend);
ContactModel (VcardModel *vcard);
static const char *NAME; static const char *NAME;
......
...@@ -109,8 +109,7 @@ bool VcardModel::setAvatar (const QString &path) { ...@@ -109,8 +109,7 @@ bool VcardModel::setAvatar (const QString &path) {
} }
// 4. Update. // 4. Update.
shared_ptr<belcard::BelCardPhoto> photo = shared_ptr<belcard::BelCardPhoto> photo = belcard::BelCardGeneric::create<belcard::BelCardPhoto>();
belcard::BelCardGeneric::create<belcard::BelCardPhoto>();
photo->setValue(VCARD_SCHEME + ::Utils::qStringToLinphoneString(file_id)); photo->setValue(VCARD_SCHEME + ::Utils::qStringToLinphoneString(file_id));
if (!belcard->addPhoto(photo)) if (!belcard->addPhoto(photo))
...@@ -145,8 +144,7 @@ bool VcardModel::setAddress (const QVariantMap &address) { ...@@ -145,8 +144,7 @@ bool VcardModel::setAddress (const QVariantMap &address) {
while (!addresses.empty()) while (!addresses.empty())
belcard->removeAddress(addresses.front()); belcard->removeAddress(addresses.front());
shared_ptr<belcard::BelCardAddress> belcard_address = shared_ptr<belcard::BelCardAddress> belcard_address = belcard::BelCardGeneric::create<belcard::BelCardAddress>();
belcard::BelCardGeneric::create<belcard::BelCardAddress>();
belcard_address->setStreet(::Utils::qStringToLinphoneString(address["street"].toString())); belcard_address->setStreet(::Utils::qStringToLinphoneString(address["street"].toString()));
belcard_address->setLocality(::Utils::qStringToLinphoneString(address["locality"].toString())); belcard_address->setLocality(::Utils::qStringToLinphoneString(address["locality"].toString()));
...@@ -163,42 +161,35 @@ bool VcardModel::setAddress (const QVariantMap &address) { ...@@ -163,42 +161,35 @@ bool VcardModel::setAddress (const QVariantMap &address) {
QVariantList VcardModel::getSipAddresses () const { QVariantList VcardModel::getSipAddresses () const {
QVariantList list; QVariantList list;
for (const auto &address : m_vcard->getSipAddresses()) for (const auto &address : m_vcard->getBelcard()->getImpp())
list.append(::Utils::linphoneStringToQString(address->asString())); list.append(::Utils::linphoneStringToQString(address->getValue()));
return list; return list;
} }
bool VcardModel::addSipAddress (const QString &sip_address) { bool VcardModel::addSipAddress (const QString &sip_address) {
shared_ptr<linphone::Address> address = CoreManager::getInstance()->getCore()->createAddress( shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
::Utils::qStringToLinphoneString(sip_address) shared_ptr<belcard::BelCardImpp> value = belcard::BelCardGeneric::create<belcard::BelCardImpp>();
); value->setValue(::Utils::qStringToLinphoneString(sip_address));
qInfo() << QStringLiteral("Add new sip address: `%1`.").arg(sip_address);
if (!address) { if (!belcard->addImpp(value)) {
qWarning() << QStringLiteral("Unable to add invalid sip address: `%1`.").arg(sip_address); qWarning() << QStringLiteral("Unable to add sip address: `%1`.").arg(sip_address);
return false; return false;
} }
qInfo() << QStringLiteral("Add new sip address: `%1`.").arg(sip_address);
m_vcard->addSipAddress(address->asStringUriOnly());
emit vcardUpdated(); emit vcardUpdated();
return true; return true;
} }
void VcardModel::removeSipAddress (const QString &sip_address) { void VcardModel::removeSipAddress (const QString &sip_address) {
list<shared_ptr<linphone::Address> > addresses = m_vcard->getSipAddresses(); shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
string match = ::Utils::qStringToLinphoneString(sip_address); list<shared_ptr<belcard::BelCardImpp> > addresses = belcard->getImpp();
shared_ptr<belcard::BelCardImpp> value = findBelCardValue(addresses, sip_address);
auto it = find_if(
addresses.cbegin(), addresses.cend(), [&match](const shared_ptr<linphone::Address> &address) {
return match == address->asString();
}
);
if (it == addresses.cend()) { if (!value) {
qWarning() << QStringLiteral("Unable to found sip address: `%1`.") qWarning() << QStringLiteral("Unable to remove sip address: `%1`.").arg(sip_address);
.arg(sip_address);
return; return;
} }
...@@ -209,7 +200,7 @@ void VcardModel::removeSipAddress (const QString &sip_address) { ...@@ -209,7 +200,7 @@ void VcardModel::removeSipAddress (const QString &sip_address) {
} }
qInfo() << QStringLiteral("Remove sip address: `%1`.").arg(sip_address); qInfo() << QStringLiteral("Remove sip address: `%1`.").arg(sip_address);
m_vcard->removeSipAddress((*it)->asStringUriOnly()); belcard->removeImpp(value);
emit vcardUpdated(); emit vcardUpdated();
} }
...@@ -241,8 +232,10 @@ bool VcardModel::addCompany (const QString &company) { ...@@ -241,8 +232,10 @@ bool VcardModel::addCompany (const QString &company) {
qInfo() << QStringLiteral("Add new company: `%1`.").arg(company); qInfo() << QStringLiteral("Add new company: `%1`.").arg(company);
if (!belcard->addRole(value)) if (!belcard->addRole(value)) {
qWarning() << QStringLiteral("Unable to add company: `%1`.").arg(company);
return false; return false;
}
emit vcardUpdated(); emit vcardUpdated();
return true; return true;
...@@ -285,14 +278,15 @@ QVariantList VcardModel::getEmails () const { ...@@ -285,14 +278,15 @@ QVariantList VcardModel::getEmails () const {
bool VcardModel::addEmail (const QString &email) { bool VcardModel::addEmail (const QString &email) {
shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard(); shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
shared_ptr<belcard::BelCardEmail> value = shared_ptr<belcard::BelCardEmail> value = belcard::BelCardGeneric::create<belcard::BelCardEmail>();
belcard::BelCardGeneric::create<belcard::BelCardEmail>();
value->setValue(::Utils::qStringToLinphoneString(email)); value->setValue(::Utils::qStringToLinphoneString(email));
qInfo() << QStringLiteral("Add new email: `%1`.").arg(email); qInfo() << QStringLiteral("Add new email: `%1`.").arg(email);
if (!belcard->addEmail(value)) if (!belcard->addEmail(value)) {
qWarning() << QStringLiteral("Unable to add email: `%1`.").arg(email);
return false; return false;
}
emit vcardUpdated(); emit vcardUpdated();
return true; return true;
...@@ -340,8 +334,10 @@ bool VcardModel::addUrl (const QString &url) { ...@@ -340,8 +334,10 @@ bool VcardModel::addUrl (const QString &url) {
qInfo() << QStringLiteral("Add new url: `%1`.").arg(url); qInfo() << QStringLiteral("Add new url: `%1`.").arg(url);
if (!belcard->addURL(value)) if (!belcard->addURL(value)) {
qWarning() << QStringLiteral("Unable to add url: `%1`.").arg(url);
return false; return false;
}
emit vcardUpdated(); emit vcardUpdated();
return true; return true;
......
...@@ -17,11 +17,13 @@ class VcardModel : public QObject { ...@@ -17,11 +17,13 @@ class VcardModel : public QObject {
Q_PROPERTY(QVariantList emails READ getEmails NOTIFY vcardUpdated); Q_PROPERTY(QVariantList emails READ getEmails NOTIFY vcardUpdated);
Q_PROPERTY(QVariantList urls READ getUrls NOTIFY vcardUpdated); Q_PROPERTY(QVariantList urls READ getUrls NOTIFY vcardUpdated);
friend class ContactsListProxyModel; friend class ContactModel;
public: public:
VcardModel (std::shared_ptr<linphone::Vcard> vcard) : m_vcard(vcard) {} VcardModel (std::shared_ptr<linphone::Vcard> vcard) : m_vcard(vcard) {}
QString getUsername () const;
~VcardModel () = default; ~VcardModel () = default;
public slots: public slots:
...@@ -45,7 +47,6 @@ signals: ...@@ -45,7 +47,6 @@ signals:
void vcardUpdated (); void vcardUpdated ();
private: private:
QString getUsername () const;
void setUsername (const QString &username); void setUsername (const QString &username);
QString getAvatar () const; QString getAvatar () const;
......
...@@ -27,7 +27,7 @@ ContactsListModel::ContactsListModel (QObject *parent) : QAbstractListModel(pare ...@@ -27,7 +27,7 @@ ContactsListModel::ContactsListModel (QObject *parent) : QAbstractListModel(pare
} }
} }
int ContactsListModel::rowCount (const QModelIndex&) const { int ContactsListModel::rowCount (const QModelIndex &) const {
return m_list.count(); return m_list.count();
} }
...@@ -49,8 +49,8 @@ QVariant ContactsListModel::data (const QModelIndex &index, int role) const { ...@@ -49,8 +49,8 @@ QVariant ContactsListModel::data (const QModelIndex &index, int role) const {
return QVariant(); return QVariant();
} }
bool ContactsListModel::removeRow (int row, const QModelIndex&) { bool ContactsListModel::removeRow (int row, const QModelIndex &parent) {
return removeRows(row, 1); return removeRows(row, 1, parent);
} }
bool ContactsListModel::removeRows (int row, int count, const QModelIndex &parent) { bool ContactsListModel::removeRows (int row, int count, const QModelIndex &parent) {
...@@ -76,19 +76,38 @@ bool ContactsListModel::removeRows (int row, int count, const QModelIndex &paren ...@@ -76,19 +76,38 @@ bool ContactsListModel::removeRows (int row, int count, const QModelIndex &paren
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
ContactModel *ContactsListModel::mapSipAddressToContact (const QString &sipAddress) const { ContactModel *ContactsListModel::mapSipAddressToContact (const QString &sipAddress) const {
// TODO: Maybe use a hashtable in future version to get a lower cost?
shared_ptr<linphone::Friend> friend_ = m_linphone_friends->findFriendByUri( shared_ptr<linphone::Friend> friend_ = m_linphone_friends->findFriendByUri(
::Utils::qStringToLinphoneString(sipAddress) ::Utils::qStringToLinphoneString(sipAddress)
); );
if (!friend_) { if (!friend_)
qInfo() << QStringLiteral("Unable to map sip address: `%1`.").arg(sipAddress);
return nullptr; return nullptr;
}
return &friend_->getData<ContactModel>(ContactModel::NAME); return &friend_->getData<ContactModel>(ContactModel::NAME);
} }
void ContactsListModel::addContact (VcardModel *vcard) {
ContactModel *contact = new ContactModel(vcard);
App::getInstance()->getEngine()->setObjectOwnership(contact, QQmlEngine::CppOwnership);
qInfo() << "Add contact:" << contact;
if (
m_linphone_friends->addFriend(contact->m_linphone_friend) !=
linphone::FriendListStatus::FriendListStatusOK
) {
qWarning() << "Unable to add friend from vcard:" << vcard;
delete contact;
return;
}
int row = rowCount();
beginInsertRows(QModelIndex(), row, row);
m_list << contact;
endInsertRows();
}
void ContactsListModel::removeContact (ContactModel *contact) { void ContactsListModel::removeContact (ContactModel *contact) {
qInfo() << "Removing contact:" << contact; qInfo() << "Removing contact:" << contact;
......
...@@ -27,6 +27,7 @@ public: ...@@ -27,6 +27,7 @@ public:
public slots: public slots:
ContactModel *mapSipAddressToContact (const QString &sipAddress) const; ContactModel *mapSipAddressToContact (const QString &sipAddress) const;
void addContact (VcardModel *vcard);
void removeContact (ContactModel *contact); void removeContact (ContactModel *contact);
private: private:
......
...@@ -6,12 +6,15 @@ ...@@ -6,12 +6,15 @@
CoreManager *CoreManager::m_instance = nullptr; CoreManager *CoreManager::m_instance = nullptr;
CoreManager::CoreManager (QObject *parent) : QObject(parent), m_core( CoreManager::CoreManager (QObject *parent) : QObject(parent),
linphone::Factory::get()->createCore(nullptr, "", "", nullptr) m_core(linphone::Factory::get()->createCore(nullptr, "", "", nullptr)) {
) {
setDatabasesPaths(); setDatabasesPaths();
} }
VcardModel *CoreManager::createDetachedVcardModel () {
return new VcardModel(linphone::Factory::get()->createVcard());
}
void CoreManager::setDatabasesPaths () { void CoreManager::setDatabasesPaths () {
std::string database_path; std::string database_path;
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include <QObject> #include <QObject>
#include <linphone++/linphone.hh> #include <linphone++/linphone.hh>
#include "../contact/VcardModel.hpp"
// =================================================================== // ===================================================================
class CoreManager : public QObject { class CoreManager : public QObject {
...@@ -24,14 +26,16 @@ public: ...@@ -24,14 +26,16 @@ public:
return m_core; return m_core;
} }
public slots:
VcardModel *createDetachedVcardModel ();
private: private:
CoreManager (QObject *parent = Q_NULLPTR); CoreManager (QObject *parent = Q_NULLPTR);
void setDatabasesPaths (); void setDatabasesPaths ();
static CoreManager *m_instance;
std::shared_ptr<linphone::Core> m_core; std::shared_ptr<linphone::Core> m_core;
static CoreManager *m_instance;
}; };
#endif // CORE_MANAGER_H_ #endif // CORE_MANAGER_H_
pragma Singleton pragma Singleton
import QtQuick 2.7 import QtQuick 2.7
// =================================================================== // =============================================================================
QtObject { QtObject {
property color a: 'transparent' property color a: 'transparent'
property color o: '#232323' // TextButtonA Hovered.
property color q: '#E6E6E6' property color q: '#E6E6E6'
property color d: '#5A585B' property color d: '#5A585B'
...@@ -24,6 +22,7 @@ QtObject { ...@@ -24,6 +22,7 @@ QtObject {
property color g20: '#336B7A86' property color g20: '#336B7A86'
property color h: '#687680' property color h: '#687680'
property color i: '#FE5E00' property color i: '#FE5E00'
property color i30: '#4DFE5E00'
property color j: '#4B5964' property color j: '#4B5964'
property color j75: '#BF4B5964' property color j75: '#BF4B5964'
property color k: '#FFFFFF' property color k: '#FFFFFF'
...@@ -31,6 +30,7 @@ QtObject { ...@@ -31,6 +30,7 @@ QtObject {
property color l: '#000000' property color l: '#000000'
property color m: '#D1D1D1' property color m: '#D1D1D1'
property color n: '#C0C0C0' property color n: '#C0C0C0'
property color o: '#232323'
property color p: '#E2E9EF' property color p: '#E2E9EF'
property color r: '#595759' property color r: '#595759'
property color s: '#D64D00' property color s: '#D64D00'
......
...@@ -3,48 +3,79 @@ import QtQuick.Controls 2.0 ...@@ -3,48 +3,79 @@ import QtQuick.Controls 2.0
import Common.Styles 1.0 import Common.Styles 1.0
// =================================================================== // =============================================================================
Button { Item {
id: button id: wrappedButton
property color colorDisabled
property color colorHovered property color colorHovered
property color colorNormal property color colorNormal
property color colorPressed property color colorPressed
// By default textColorNormal is the hovered/pressed text color. // By default textColorNormal is the hovered/pressed text color.
property color textColorDisabled
property color textColorHovered: textColorNormal property color textColorHovered: textColorNormal
property color textColorNormal property color textColorNormal
property color textColorPressed: textColorNormal property color textColorPressed: textColorNormal
background: Rectangle { property alias text: button.text
color: button.down property bool enabled: true
signal clicked
// ---------------------------------------------------------------------------
function _getBackgroundColor () {
if (!wrappedButton.enabled) {
return colorDisabled
}
return button.down
? colorPressed ? colorPressed
: (button.hovered : (button.hovered ? colorHovered : colorNormal)
? colorHovered
: colorNormal
)
implicitHeight: AbstractTextButtonStyle.background.height
implicitWidth: AbstractTextButtonStyle.background.width
radius: AbstractTextButtonStyle.background.radius
} }
contentItem: Text {
color: button.down function _getTextColor () {
if (!wrappedButton.enabled) {
return textColorDisabled
}
return button.down
? textColorPressed ? textColorPressed
: (button.hovered : (button.hovered ? textColorHovered : textColorNormal)
? textColorHovered }
: textColorNormal
) // ---------------------------------------------------------------------------
font { implicitHeight: button.height
bold: true implicitWidth: button.width
pointSize: AbstractTextButtonStyle.text.fontSize
// ---------------------------------------------------------------------------
Button {
id: button
background: Rectangle {
color: _getBackgroundColor()
implicitHeight: AbstractTextButtonStyle.background.height
implicitWidth: AbstractTextButtonStyle.background.width
radius: AbstractTextButtonStyle.background.radius
}
contentItem: Text {
color: _getTextColor()
font {
bold: true
pointSize: AbstractTextButtonStyle.text.fontSize
}
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: button.text
verticalAlignment: Text.AlignVCenter
} }
hoverEnabled: true
elide: Text.ElideRight onClicked: wrappedButton.enabled && parent.clicked()
horizontalAlignment: Text.AlignHCenter
text: button.text
verticalAlignment: Text.AlignVCenter
} }
hoverEnabled: true
} }
...@@ -5,21 +5,23 @@ import Common 1.0 ...@@ -5,21 +5,23 @@ import Common 1.0
import Common.Styles 1.0 import Common.Styles 1.0
import Utils 1.0 import Utils 1.0
// =================================================================== // =============================================================================
RowLayout { RowLayout {
id: listForm id: listForm
property alias placeholder: placeholder.text property alias placeholder: placeholder.text
property alias title: text.text property alias title: text.text
property bool readOnly: false
property int inputMethodHints property int inputMethodHints
property var defaultData: [] property var defaultData: []
property var minValues property var minValues
readonly property int count: values.count
signal changed (int index, string default_value, string new_value) signal changed (int index, string defaultValue, string newValue)
signal removed (int index, string value) signal removed (int index, string value)
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
function setInvalid (index, status) { function setInvalid (index, status) {
Utils.assert( Utils.assert(
...@@ -48,47 +50,41 @@ RowLayout { ...@@ -48,47 +50,41 @@ RowLayout {
} }
function _handleEditionFinished (index, text) { function _handleEditionFinished (index, text) {
if (text.length === 0) { var model = values.model
// Remove. var defaultValue = model.get(index).$value
var default_value = values.model.get(index).$value
if (minValues != null && minValues >= values.model.count) {
var model = values.model
if (text.length === 0) {
// No changes. It must exists at least n min values.
if (minValues != null && minValues >= model.count) {
// Unable to set property directly. Qt uses a cache of the value. // Unable to set property directly. Qt uses a cache of the value.
model.remove(index) model.remove(index)
model.insert(index, { model.insert(index, {
$isInvalid: false, $isInvalid: false,
$value: default_value $value: defaultValue
}) })
return return
} }
values.model.remove(index) model.remove(index)
if (default_value.length !== 0) { if (defaultValue.length !== 0) {
listForm.removed(index, default_value) listForm.removed(index, defaultValue)
}
} else {
// Update.
var default_value = values.model.get(index).$value
// If no changes, no signal.
if (text !== default_value) {
listForm.changed(index, default_value, text)
} }
} else if (text !== defaultValue) {
// Update changes.
listForm.changed(index, defaultValue, text)
} }
addButton.enabled = true addButton.enabled = true
} }
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
spacing: 0 spacing: 0
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
// Title area. // Title area.
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
RowLayout { RowLayout {
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
...@@ -100,8 +96,9 @@ RowLayout { ...@@ -100,8 +96,9 @@ RowLayout {
icon: 'add' icon: 'add'
iconSize: ListFormStyle.titleArea.iconSize iconSize: ListFormStyle.titleArea.iconSize
opacity: _edition ? 1 : 0
onClicked: _addValue('') onClicked: _edition && _addValue('')
} }
Text { Text {
...@@ -118,9 +115,9 @@ RowLayout { ...@@ -118,9 +115,9 @@ RowLayout {
} }
} }
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
// Placeholder. // Placeholder.
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
Text { Text {
id: placeholder id: placeholder
...@@ -135,7 +132,7 @@ RowLayout { ...@@ -135,7 +132,7 @@ RowLayout {
} }
padding: ListFormStyle.value.text.padding padding: ListFormStyle.value.text.padding
visible: values.model.count === 0 visible: values.model.count === 0 && !listForm.readOnly
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
MouseArea { MouseArea {
...@@ -144,9 +141,9 @@ RowLayout { ...@@ -144,9 +141,9 @@ RowLayout {
} }
} }
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
// Values. // Values.
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
ListView { ListView {
id: values id: values
...@@ -165,6 +162,7 @@ RowLayout { ...@@ -165,6 +162,7 @@ RowLayout {
inputMethodHints: listForm.inputMethodHints inputMethodHints: listForm.inputMethodHints
isInvalid: $isInvalid isInvalid: $isInvalid
readOnly: listForm.readOnly
text: $value text: $value
height: ListFormStyle.lineHeight height: ListFormStyle.lineHeight
...@@ -190,16 +188,14 @@ RowLayout { ...@@ -190,16 +188,14 @@ RowLayout {
model: ListModel {} model: ListModel {}
// --------------------------------------------------------------- // -------------------------------------------------------------------------
// Init values. // Init values.
// --------------------------------------------------------------- // -------------------------------------------------------------------------
Component.onCompleted: { Component.onCompleted: {
if (!defaultData) { if (defaultData) {
return setData(defaultData)
} }
setData(defaultData)
} }
} }
} }
import Common.Styles 1.0 import Common.Styles 1.0
// =================================================================== // =============================================================================
AbstractTextButton { AbstractTextButton {
colorDisabled: TextButtonAStyle.backgroundColor.disabled
colorHovered: TextButtonAStyle.backgroundColor.hovered colorHovered: TextButtonAStyle.backgroundColor.hovered
colorNormal: TextButtonAStyle.backgroundColor.normal colorNormal: TextButtonAStyle.backgroundColor.normal
colorPressed: TextButtonAStyle.backgroundColor.pressed colorPressed: TextButtonAStyle.backgroundColor.pressed
textColorDisabled: TextButtonAStyle.textColor.disabled
textColorHovered: TextButtonAStyle.textColor.hovered textColorHovered: TextButtonAStyle.textColor.hovered
textColorNormal: TextButtonAStyle.textColor.normal textColorNormal: TextButtonAStyle.textColor.normal
textColorPressed: TextButtonAStyle.textColor.pressed textColorPressed: TextButtonAStyle.textColor.pressed
......
import Common.Styles 1.0 import Common.Styles 1.0
// =================================================================== // =============================================================================
AbstractTextButton { AbstractTextButton {
colorDisabled: TextButtonBStyle.backgroundColor.disabled
colorHovered: TextButtonBStyle.backgroundColor.hovered colorHovered: TextButtonBStyle.backgroundColor.hovered
colorNormal: TextButtonBStyle.backgroundColor.normal colorNormal: TextButtonBStyle.backgroundColor.normal
colorPressed: TextButtonBStyle.backgroundColor.pressed colorPressed: TextButtonBStyle.backgroundColor.pressed
textColorDisabled: TextButtonBStyle.textColor.disabled
textColorHovered: TextButtonBStyle.textColor.hovered textColorHovered: TextButtonBStyle.textColor.hovered
textColorNormal: TextButtonBStyle.textColor.normal textColorNormal: TextButtonBStyle.textColor.normal
textColorPressed: TextButtonBStyle.textColor.pressed textColorPressed: TextButtonBStyle.textColor.pressed
......
...@@ -3,16 +3,18 @@ import QtQuick 2.7 ...@@ -3,16 +3,18 @@ import QtQuick 2.7
import Common 1.0 import Common 1.0
// =================================================================== // =============================================================================
QtObject { QtObject {
property QtObject backgroundColor: QtObject { property QtObject backgroundColor: QtObject {
property color disabled: Colors.o
property color hovered: Colors.o property color hovered: Colors.o
property color normal: Colors.j property color normal: Colors.j
property color pressed: Colors.i property color pressed: Colors.i
} }
property QtObject textColor: QtObject { property QtObject textColor: QtObject {
property color disabled: Colors.k
property color hovered: Colors.k property color hovered: Colors.k
property color normal: Colors.k property color normal: Colors.k
property color pressed: Colors.k property color pressed: Colors.k
......
...@@ -3,16 +3,18 @@ import QtQuick 2.7 ...@@ -3,16 +3,18 @@ import QtQuick 2.7
import Common 1.0 import Common 1.0
// =================================================================== // =============================================================================
QtObject { QtObject {
property QtObject backgroundColor: QtObject { property QtObject backgroundColor: QtObject {
property color disabled: Colors.i30
property color hovered: Colors.s property color hovered: Colors.s
property color normal: Colors.i property color normal: Colors.i
property color pressed: Colors.t property color pressed: Colors.t
} }
property QtObject textColor: QtObject { property QtObject textColor: QtObject {
property color disabled: Colors.k
property color hovered: Colors.k property color hovered: Colors.k
property color normal: Colors.k property color normal: Colors.k
property color pressed: Colors.k property color pressed: Colors.k
......
...@@ -5,22 +5,45 @@ import QtQuick.Layouts 1.3 ...@@ -5,22 +5,45 @@ import QtQuick.Layouts 1.3
import Common 1.0 import Common 1.0
import Linphone 1.0 import Linphone 1.0
import LinphoneUtils 1.0
import Utils 1.0 import Utils 1.0
import App.Styles 1.0 import App.Styles 1.0
// =================================================================== // =============================================================================
ColumnLayout { ColumnLayout {
id: contactEdit id: contactEdit
property string sipAddress: '' property string sipAddress
property bool _edition: false
property var _contact property var _contact
property var _vcard property var _vcard
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
function _editContact () {
_contact.startEdit()
_edition = true
}
function _save () {
if (_contact) {
_contact.endEdit()
_edition = false
} else {
_contact = ContactsListModel.addContact(_vcard)
}
}
function _cancel () {
if (_contact) {
_contact.abortEdit()
_edition = false
} else {
window.setView('Contacts')
}
}
function _removeContact () { function _removeContact () {
Utils.openConfirmDialog(window, { Utils.openConfirmDialog(window, {
...@@ -46,45 +69,55 @@ ColumnLayout { ...@@ -46,45 +69,55 @@ ColumnLayout {
usernameInput.text = _vcard.username usernameInput.text = _vcard.username
} }
function _handleSipAddressChanged (index, default_value, new_value) { function _handleSipAddressChanged (index, defaultValue, newValue) {
if (!Utils.startsWith(new_value, 'sip:')) { if (!Utils.startsWith(newValue, 'sip:')) {
new_value = 'sip:' + new_value newValue = 'sip:' + newValue
if (new_value === default_value) { if (newValue === defaultValue) {
return return
} }
} }
var so_far_so_good = (default_value.length === 0) var so_far_so_good = (defaultValue.length === 0)
? _vcard.addSipAddress(new_value) ? _vcard.addSipAddress(newValue)
: _vcard.updateSipAddress(default_value, new_value) : _vcard.updateSipAddress(defaultValue, newValue)
addresses.setInvalid(index, !so_far_so_good) addresses.setInvalid(index, !so_far_so_good)
} }
function _handleUrlChanged (index, default_value, new_value) { function _handleUrlChanged (index, defaultValue, newValue) {
var url = Utils.extractFirstUri(new_value) var url = Utils.extractFirstUri(newValue)
if (url === default_value) { if (url === defaultValue) {
return return
} }
var so_far_so_good = (default_value.length === 0) var so_far_so_good = (defaultValue.length === 0)
? url && _vcard.addUrl(new_value) ? url && _vcard.addUrl(newValue)
: url && _vcard.updateUrl(default_value, new_value) : url && _vcard.updateUrl(defaultValue, newValue)
urls.setInvalid(index, !so_far_so_good) urls.setInvalid(index, !so_far_so_good)
} }
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
spacing: 0 spacing: 0
Component.onCompleted: { Component.onCompleted: {
_contact = ContactsListModel.mapSipAddressToContact(sipAddress) _contact = ContactsListModel.mapSipAddressToContact(sipAddress)
_vcard = _contact.vcard
if (!_contact) {
_vcard = CoreManager.createDetachedVcardModel()
_edition = true
} else {
_vcard = _contact.vcard
}
} }
// ----------------------------------------------------------------- Component.onDestruction: {
// TODO: Remove photo if contact not created.
}
// ---------------------------------------------------------------------------
FileDialog { FileDialog {
id: avatarChooser id: avatarChooser
...@@ -95,9 +128,9 @@ ColumnLayout { ...@@ -95,9 +128,9 @@ ColumnLayout {
onAccepted: _setAvatar(fileUrls[0]) onAccepted: _setAvatar(fileUrls[0])
} }
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
// Info bar. // Info bar.
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
...@@ -124,7 +157,7 @@ ColumnLayout { ...@@ -124,7 +157,7 @@ ColumnLayout {
anchors.fill: parent anchors.fill: parent
image: _vcard.avatar image: _vcard.avatar
username: LinphoneUtils.getContactUsername(_contact) || 'John Doe' username: _vcard.username
visible: isLoaded() && !parent.hovered visible: isLoaded() && !parent.hovered
} }
} }
...@@ -155,22 +188,30 @@ ColumnLayout { ...@@ -155,22 +188,30 @@ ColumnLayout {
ActionButton { ActionButton {
icon: 'history' icon: 'history'
onClicked: window.setView('Conversation', { onClicked: window.setView('Conversation', {
sipAddress: contactEdit.sipAddress sipAddress: contactEdit.sipAddress
}) })
} }
ActionButton {
icon: 'edit'
visible: !_edition
onClicked: _editContact()
}
ActionButton { ActionButton {
icon: 'delete' icon: 'delete'
onClicked: _removeContact() onClicked: _removeContact()
} }
} }
} }
} }
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
// Info list. // Info list.
// ----------------------------------------------------------------- // ---------------------------------------------------------------------------
Flickable { Flickable {
id: flick id: flick
...@@ -197,12 +238,13 @@ ColumnLayout { ...@@ -197,12 +238,13 @@ ColumnLayout {
Layout.rightMargin: ContactEditStyle.values.rightMargin Layout.rightMargin: ContactEditStyle.values.rightMargin
Layout.topMargin: ContactEditStyle.values.topMargin Layout.topMargin: ContactEditStyle.values.topMargin
defaultData: _vcard.sipAddresses defaultData: _vcard.sipAddresses
minValues: 1 minValues: _contact ? 1 : 0
placeholder: qsTr('sipAccountsInput') placeholder: qsTr('sipAccountsInput')
readOnly: !_edition
title: qsTr('sipAccounts') title: qsTr('sipAccounts')
onChanged: _handleSipAddressChanged(index, default_value, new_value) onChanged: _handleSipAddressChanged(index, defaultValue, newValue)
onRemoved: _vcard.removeSipAddress(value) onRemoved: _vcard.removeSipAddress(value)
} }
...@@ -220,11 +262,12 @@ ColumnLayout { ...@@ -220,11 +262,12 @@ ColumnLayout {
defaultData: _vcard.companies defaultData: _vcard.companies
placeholder: qsTr('companiesInput') placeholder: qsTr('companiesInput')
readOnly: !_edition
title: qsTr('companies') title: qsTr('companies')
onChanged: default_value.length === 0 onChanged: defaultValue.length === 0
? _vcard.addCompany(new_value) ? _vcard.addCompany(newValue)
: _vcard.updateCompany(default_value, new_value) : _vcard.updateCompany(defaultValue, newValue)
onRemoved: _vcard.removeCompany(value) onRemoved: _vcard.removeCompany(value)
} }
...@@ -243,11 +286,12 @@ ColumnLayout { ...@@ -243,11 +286,12 @@ ColumnLayout {
defaultData: _vcard.emails defaultData: _vcard.emails
inputMethodHints: Qt.ImhEmailCharactersOnly inputMethodHints: Qt.ImhEmailCharactersOnly
placeholder: qsTr('emailsInput') placeholder: qsTr('emailsInput')
readOnly: !_edition
title: qsTr('emails') title: qsTr('emails')
onChanged: default_value.length === 0 onChanged: defaultValue.length === 0
? _vcard.addEmail(new_value) ? _vcard.addEmail(newValue)
: _vcard.updateEmail(default_value, new_value) : _vcard.updateEmail(defaultValue, newValue)
onRemoved: _vcard.removeEmail(value) onRemoved: _vcard.removeEmail(value)
} }
...@@ -266,9 +310,10 @@ ColumnLayout { ...@@ -266,9 +310,10 @@ ColumnLayout {
defaultData: _vcard.urls defaultData: _vcard.urls
inputMethodHints: Qt.ImhUrlCharactersOnly inputMethodHints: Qt.ImhUrlCharactersOnly
placeholder: qsTr('webSitesInput') placeholder: qsTr('webSitesInput')
readOnly: !_edition
title: qsTr('webSites') title: qsTr('webSites')
onChanged: _handleUrlChanged(index, default_value, new_value) onChanged: _handleUrlChanged(index, defaultValue, newValue)
onRemoved: _vcard.removeUrl(value) onRemoved: _vcard.removeUrl(value)
} }
...@@ -278,20 +323,21 @@ ColumnLayout { ...@@ -278,20 +323,21 @@ ColumnLayout {
color: ContactEditStyle.values.separator.color color: ContactEditStyle.values.separator.color
} }
Loader { Row {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: ContactEditStyle.buttons.topMargin Layout.topMargin: ContactEditStyle.buttons.topMargin
spacing: ContactEditStyle.buttons.spacing
visible: _edition
sourceComponent: Row { TextButtonB {
spacing: ContactEditStyle.buttons.spacing enabled: _vcard.sipAddresses.length > 0
text: qsTr('save')
TextButtonB { onClicked: _save()
text: qsTr('save') }
}
TextButtonA { TextButtonA {
text: qsTr('cancel') text: qsTr('cancel')
} onClicked: _cancel()
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment