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

This commit is contained in:
Ronan Abhamon 2016-12-15 14:26:50 +01:00
parent 338fe8c074
commit d308d0a347
20 changed files with 325 additions and 163 deletions

View file

@ -0,0 +1,13 @@
<?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>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,13 @@
<?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>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,13 @@
<?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>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -53,6 +53,9 @@
<file>assets/images/delete_hovered.svg</file>
<file>assets/images/delete_normal.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/filter.svg</file>
<file>assets/images/fullscreen_hovered.svg</file>

View file

@ -1,3 +1,5 @@
#include <QtDebug>
#include "../../app/App.hpp"
#include "ContactModel.hpp"
@ -16,6 +18,18 @@ ContactModel::ContactModel (shared_ptr<linphone::Friend> linphone_friend) {
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 {
return Presence::PresenceStatus::Offline;
}

View file

@ -22,6 +22,7 @@ class ContactModel : public QObject {
public:
ContactModel (std::shared_ptr<linphone::Friend> linphone_friend);
ContactModel (VcardModel *vcard);
static const char *NAME;

View file

@ -109,8 +109,7 @@ bool VcardModel::setAvatar (const QString &path) {
}
// 4. Update.
shared_ptr<belcard::BelCardPhoto> photo =
belcard::BelCardGeneric::create<belcard::BelCardPhoto>();
shared_ptr<belcard::BelCardPhoto> photo = belcard::BelCardGeneric::create<belcard::BelCardPhoto>();
photo->setValue(VCARD_SCHEME + ::Utils::qStringToLinphoneString(file_id));
if (!belcard->addPhoto(photo))
@ -145,8 +144,7 @@ bool VcardModel::setAddress (const QVariantMap &address) {
while (!addresses.empty())
belcard->removeAddress(addresses.front());
shared_ptr<belcard::BelCardAddress> belcard_address =
belcard::BelCardGeneric::create<belcard::BelCardAddress>();
shared_ptr<belcard::BelCardAddress> belcard_address = belcard::BelCardGeneric::create<belcard::BelCardAddress>();
belcard_address->setStreet(::Utils::qStringToLinphoneString(address["street"].toString()));
belcard_address->setLocality(::Utils::qStringToLinphoneString(address["locality"].toString()));
@ -163,42 +161,35 @@ bool VcardModel::setAddress (const QVariantMap &address) {
QVariantList VcardModel::getSipAddresses () const {
QVariantList list;
for (const auto &address : m_vcard->getSipAddresses())
list.append(::Utils::linphoneStringToQString(address->asString()));
for (const auto &address : m_vcard->getBelcard()->getImpp())
list.append(::Utils::linphoneStringToQString(address->getValue()));
return list;
}
bool VcardModel::addSipAddress (const QString &sip_address) {
shared_ptr<linphone::Address> address = CoreManager::getInstance()->getCore()->createAddress(
::Utils::qStringToLinphoneString(sip_address)
);
if (!address) {
qWarning() << QStringLiteral("Unable to add invalid sip address: `%1`.").arg(sip_address);
return false;
}
shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
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);
m_vcard->addSipAddress(address->asStringUriOnly());
if (!belcard->addImpp(value)) {
qWarning() << QStringLiteral("Unable to add sip address: `%1`.").arg(sip_address);
return false;
}
emit vcardUpdated();
return true;
}
void VcardModel::removeSipAddress (const QString &sip_address) {
list<shared_ptr<linphone::Address> > addresses = m_vcard->getSipAddresses();
string match = ::Utils::qStringToLinphoneString(sip_address);
shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
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()) {
qWarning() << QStringLiteral("Unable to found sip address: `%1`.")
.arg(sip_address);
if (!value) {
qWarning() << QStringLiteral("Unable to remove sip address: `%1`.").arg(sip_address);
return;
}
@ -209,7 +200,7 @@ void VcardModel::removeSipAddress (const QString &sip_address) {
}
qInfo() << QStringLiteral("Remove sip address: `%1`.").arg(sip_address);
m_vcard->removeSipAddress((*it)->asStringUriOnly());
belcard->removeImpp(value);
emit vcardUpdated();
}
@ -241,8 +232,10 @@ bool VcardModel::addCompany (const QString &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;
}
emit vcardUpdated();
return true;
@ -285,14 +278,15 @@ QVariantList VcardModel::getEmails () const {
bool VcardModel::addEmail (const QString &email) {
shared_ptr<belcard::BelCard> belcard = m_vcard->getBelcard();
shared_ptr<belcard::BelCardEmail> value =
belcard::BelCardGeneric::create<belcard::BelCardEmail>();
shared_ptr<belcard::BelCardEmail> value = belcard::BelCardGeneric::create<belcard::BelCardEmail>();
value->setValue(::Utils::qStringToLinphoneString(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;
}
emit vcardUpdated();
return true;
@ -340,8 +334,10 @@ bool VcardModel::addUrl (const QString &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;
}
emit vcardUpdated();
return true;

View file

@ -17,11 +17,13 @@ class VcardModel : public QObject {
Q_PROPERTY(QVariantList emails READ getEmails NOTIFY vcardUpdated);
Q_PROPERTY(QVariantList urls READ getUrls NOTIFY vcardUpdated);
friend class ContactsListProxyModel;
friend class ContactModel;
public:
VcardModel (std::shared_ptr<linphone::Vcard> vcard) : m_vcard(vcard) {}
QString getUsername () const;
~VcardModel () = default;
public slots:
@ -45,7 +47,6 @@ signals:
void vcardUpdated ();
private:
QString getUsername () const;
void setUsername (const QString &username);
QString getAvatar () const;

View file

@ -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();
}
@ -49,8 +49,8 @@ QVariant ContactsListModel::data (const QModelIndex &index, int role) const {
return QVariant();
}
bool ContactsListModel::removeRow (int row, const QModelIndex&) {
return removeRows(row, 1);
bool ContactsListModel::removeRow (int row, const QModelIndex &parent) {
return removeRows(row, 1, 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
// -----------------------------------------------------------------------------
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(
::Utils::qStringToLinphoneString(sipAddress)
);
if (!friend_) {
qInfo() << QStringLiteral("Unable to map sip address: `%1`.").arg(sipAddress);
if (!friend_)
return nullptr;
}
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) {
qInfo() << "Removing contact:" << contact;

View file

@ -27,6 +27,7 @@ public:
public slots:
ContactModel *mapSipAddressToContact (const QString &sipAddress) const;
void addContact (VcardModel *vcard);
void removeContact (ContactModel *contact);
private:

View file

@ -6,12 +6,15 @@
CoreManager *CoreManager::m_instance = nullptr;
CoreManager::CoreManager (QObject *parent) : QObject(parent), m_core(
linphone::Factory::get()->createCore(nullptr, "", "", nullptr)
) {
CoreManager::CoreManager (QObject *parent) : QObject(parent),
m_core(linphone::Factory::get()->createCore(nullptr, "", "", nullptr)) {
setDatabasesPaths();
}
VcardModel *CoreManager::createDetachedVcardModel () {
return new VcardModel(linphone::Factory::get()->createVcard());
}
void CoreManager::setDatabasesPaths () {
std::string database_path;

View file

@ -4,6 +4,8 @@
#include <QObject>
#include <linphone++/linphone.hh>
#include "../contact/VcardModel.hpp"
// ===================================================================
class CoreManager : public QObject {
@ -24,14 +26,16 @@ public:
return m_core;
}
public slots:
VcardModel *createDetachedVcardModel ();
private:
CoreManager (QObject *parent = Q_NULLPTR);
void setDatabasesPaths ();
static CoreManager *m_instance;
std::shared_ptr<linphone::Core> m_core;
static CoreManager *m_instance;
};
#endif // CORE_MANAGER_H_

View file

@ -1,13 +1,11 @@
pragma Singleton
import QtQuick 2.7
// ===================================================================
// =============================================================================
QtObject {
property color a: 'transparent'
property color o: '#232323' // TextButtonA Hovered.
property color q: '#E6E6E6'
property color d: '#5A585B'
@ -24,6 +22,7 @@ QtObject {
property color g20: '#336B7A86'
property color h: '#687680'
property color i: '#FE5E00'
property color i30: '#4DFE5E00'
property color j: '#4B5964'
property color j75: '#BF4B5964'
property color k: '#FFFFFF'
@ -31,6 +30,7 @@ QtObject {
property color l: '#000000'
property color m: '#D1D1D1'
property color n: '#C0C0C0'
property color o: '#232323'
property color p: '#E2E9EF'
property color r: '#595759'
property color s: '#D64D00'

View file

@ -3,48 +3,79 @@ import QtQuick.Controls 2.0
import Common.Styles 1.0
// ===================================================================
// =============================================================================
Button {
id: button
Item {
id: wrappedButton
property color colorDisabled
property color colorHovered
property color colorNormal
property color colorPressed
// By default textColorNormal is the hovered/pressed text color.
property color textColorDisabled
property color textColorHovered: textColorNormal
property color textColorNormal
property color textColorPressed: textColorNormal
background: Rectangle {
color: button.down
? colorPressed
: (button.hovered
? colorHovered
: colorNormal
)
implicitHeight: AbstractTextButtonStyle.background.height
implicitWidth: AbstractTextButtonStyle.background.width
radius: AbstractTextButtonStyle.background.radius
}
contentItem: Text {
color: button.down
? textColorPressed
: (button.hovered
? textColorHovered
: textColorNormal
)
property alias text: button.text
property bool enabled: true
font {
bold: true
pointSize: AbstractTextButtonStyle.text.fontSize
signal clicked
// ---------------------------------------------------------------------------
function _getBackgroundColor () {
if (!wrappedButton.enabled) {
return colorDisabled
}
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: button.text
verticalAlignment: Text.AlignVCenter
return button.down
? colorPressed
: (button.hovered ? colorHovered : colorNormal)
}
function _getTextColor () {
if (!wrappedButton.enabled) {
return textColorDisabled
}
return button.down
? textColorPressed
: (button.hovered ? textColorHovered : textColorNormal)
}
// ---------------------------------------------------------------------------
implicitHeight: button.height
implicitWidth: button.width
// ---------------------------------------------------------------------------
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
onClicked: wrappedButton.enabled && parent.clicked()
}
hoverEnabled: true
}

View file

@ -5,21 +5,23 @@ import Common 1.0
import Common.Styles 1.0
import Utils 1.0
// ===================================================================
// =============================================================================
RowLayout {
id: listForm
property alias placeholder: placeholder.text
property alias title: text.text
property bool readOnly: false
property int inputMethodHints
property var defaultData: []
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)
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
function setInvalid (index, status) {
Utils.assert(
@ -48,47 +50,41 @@ RowLayout {
}
function _handleEditionFinished (index, text) {
var model = values.model
var defaultValue = model.get(index).$value
if (text.length === 0) {
// Remove.
var default_value = values.model.get(index).$value
if (minValues != null && minValues >= values.model.count) {
var model = values.model
// 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.
model.remove(index)
model.insert(index, {
$isInvalid: false,
$value: default_value
$value: defaultValue
})
return
}
values.model.remove(index)
model.remove(index)
if (default_value.length !== 0) {
listForm.removed(index, default_value)
}
} 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)
if (defaultValue.length !== 0) {
listForm.removed(index, defaultValue)
}
} else if (text !== defaultValue) {
// Update changes.
listForm.changed(index, defaultValue, text)
}
addButton.enabled = true
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
spacing: 0
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Title area.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
RowLayout {
Layout.alignment: Qt.AlignTop
@ -100,8 +96,9 @@ RowLayout {
icon: 'add'
iconSize: ListFormStyle.titleArea.iconSize
opacity: _edition ? 1 : 0
onClicked: _addValue('')
onClicked: _edition && _addValue('')
}
Text {
@ -118,9 +115,9 @@ RowLayout {
}
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Placeholder.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
Text {
id: placeholder
@ -135,7 +132,7 @@ RowLayout {
}
padding: ListFormStyle.value.text.padding
visible: values.model.count === 0
visible: values.model.count === 0 && !listForm.readOnly
verticalAlignment: Text.AlignVCenter
MouseArea {
@ -144,9 +141,9 @@ RowLayout {
}
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Values.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
ListView {
id: values
@ -165,6 +162,7 @@ RowLayout {
inputMethodHints: listForm.inputMethodHints
isInvalid: $isInvalid
readOnly: listForm.readOnly
text: $value
height: ListFormStyle.lineHeight
@ -190,16 +188,14 @@ RowLayout {
model: ListModel {}
// ---------------------------------------------------------------
// -------------------------------------------------------------------------
// Init values.
// ---------------------------------------------------------------
// -------------------------------------------------------------------------
Component.onCompleted: {
if (!defaultData) {
return
if (defaultData) {
setData(defaultData)
}
setData(defaultData)
}
}
}

View file

@ -1,12 +1,14 @@
import Common.Styles 1.0
// ===================================================================
// =============================================================================
AbstractTextButton {
colorDisabled: TextButtonAStyle.backgroundColor.disabled
colorHovered: TextButtonAStyle.backgroundColor.hovered
colorNormal: TextButtonAStyle.backgroundColor.normal
colorPressed: TextButtonAStyle.backgroundColor.pressed
textColorDisabled: TextButtonAStyle.textColor.disabled
textColorHovered: TextButtonAStyle.textColor.hovered
textColorNormal: TextButtonAStyle.textColor.normal
textColorPressed: TextButtonAStyle.textColor.pressed

View file

@ -1,12 +1,14 @@
import Common.Styles 1.0
// ===================================================================
// =============================================================================
AbstractTextButton {
colorDisabled: TextButtonBStyle.backgroundColor.disabled
colorHovered: TextButtonBStyle.backgroundColor.hovered
colorNormal: TextButtonBStyle.backgroundColor.normal
colorPressed: TextButtonBStyle.backgroundColor.pressed
textColorDisabled: TextButtonBStyle.textColor.disabled
textColorHovered: TextButtonBStyle.textColor.hovered
textColorNormal: TextButtonBStyle.textColor.normal
textColorPressed: TextButtonBStyle.textColor.pressed

View file

@ -3,16 +3,18 @@ import QtQuick 2.7
import Common 1.0
// ===================================================================
// =============================================================================
QtObject {
property QtObject backgroundColor: QtObject {
property color disabled: Colors.o
property color hovered: Colors.o
property color normal: Colors.j
property color pressed: Colors.i
}
property QtObject textColor: QtObject {
property color disabled: Colors.k
property color hovered: Colors.k
property color normal: Colors.k
property color pressed: Colors.k

View file

@ -3,16 +3,18 @@ import QtQuick 2.7
import Common 1.0
// ===================================================================
// =============================================================================
QtObject {
property QtObject backgroundColor: QtObject {
property color disabled: Colors.i30
property color hovered: Colors.s
property color normal: Colors.i
property color pressed: Colors.t
}
property QtObject textColor: QtObject {
property color disabled: Colors.k
property color hovered: Colors.k
property color normal: Colors.k
property color pressed: Colors.k

View file

@ -5,22 +5,45 @@ import QtQuick.Layouts 1.3
import Common 1.0
import Linphone 1.0
import LinphoneUtils 1.0
import Utils 1.0
import App.Styles 1.0
// ===================================================================
// =============================================================================
ColumnLayout {
id: contactEdit
property string sipAddress: ''
property string sipAddress
property bool _edition: false
property var _contact
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 () {
Utils.openConfirmDialog(window, {
@ -46,45 +69,55 @@ ColumnLayout {
usernameInput.text = _vcard.username
}
function _handleSipAddressChanged (index, default_value, new_value) {
if (!Utils.startsWith(new_value, 'sip:')) {
new_value = 'sip:' + new_value
function _handleSipAddressChanged (index, defaultValue, newValue) {
if (!Utils.startsWith(newValue, 'sip:')) {
newValue = 'sip:' + newValue
if (new_value === default_value) {
if (newValue === defaultValue) {
return
}
}
var so_far_so_good = (default_value.length === 0)
? _vcard.addSipAddress(new_value)
: _vcard.updateSipAddress(default_value, new_value)
var so_far_so_good = (defaultValue.length === 0)
? _vcard.addSipAddress(newValue)
: _vcard.updateSipAddress(defaultValue, newValue)
addresses.setInvalid(index, !so_far_so_good)
}
function _handleUrlChanged (index, default_value, new_value) {
var url = Utils.extractFirstUri(new_value)
if (url === default_value) {
function _handleUrlChanged (index, defaultValue, newValue) {
var url = Utils.extractFirstUri(newValue)
if (url === defaultValue) {
return
}
var so_far_so_good = (default_value.length === 0)
? url && _vcard.addUrl(new_value)
: url && _vcard.updateUrl(default_value, new_value)
var so_far_so_good = (defaultValue.length === 0)
? url && _vcard.addUrl(newValue)
: url && _vcard.updateUrl(defaultValue, newValue)
urls.setInvalid(index, !so_far_so_good)
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
spacing: 0
Component.onCompleted: {
_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 {
id: avatarChooser
@ -95,9 +128,9 @@ ColumnLayout {
onAccepted: _setAvatar(fileUrls[0])
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Info bar.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
Rectangle {
Layout.fillWidth: true
@ -124,7 +157,7 @@ ColumnLayout {
anchors.fill: parent
image: _vcard.avatar
username: LinphoneUtils.getContactUsername(_contact) || 'John Doe'
username: _vcard.username
visible: isLoaded() && !parent.hovered
}
}
@ -155,22 +188,30 @@ ColumnLayout {
ActionButton {
icon: 'history'
onClicked: window.setView('Conversation', {
sipAddress: contactEdit.sipAddress
})
}
ActionButton {
icon: 'edit'
visible: !_edition
onClicked: _editContact()
}
ActionButton {
icon: 'delete'
onClicked: _removeContact()
}
}
}
}
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
// Info list.
// -----------------------------------------------------------------
// ---------------------------------------------------------------------------
Flickable {
id: flick
@ -197,12 +238,13 @@ ColumnLayout {
Layout.rightMargin: ContactEditStyle.values.rightMargin
Layout.topMargin: ContactEditStyle.values.topMargin
defaultData: _vcard.sipAddresses
minValues: 1
defaultData: _vcard.sipAddresses
minValues: _contact ? 1 : 0
placeholder: qsTr('sipAccountsInput')
readOnly: !_edition
title: qsTr('sipAccounts')
onChanged: _handleSipAddressChanged(index, default_value, new_value)
onChanged: _handleSipAddressChanged(index, defaultValue, newValue)
onRemoved: _vcard.removeSipAddress(value)
}
@ -220,11 +262,12 @@ ColumnLayout {
defaultData: _vcard.companies
placeholder: qsTr('companiesInput')
readOnly: !_edition
title: qsTr('companies')
onChanged: default_value.length === 0
? _vcard.addCompany(new_value)
: _vcard.updateCompany(default_value, new_value)
onChanged: defaultValue.length === 0
? _vcard.addCompany(newValue)
: _vcard.updateCompany(defaultValue, newValue)
onRemoved: _vcard.removeCompany(value)
}
@ -243,11 +286,12 @@ ColumnLayout {
defaultData: _vcard.emails
inputMethodHints: Qt.ImhEmailCharactersOnly
placeholder: qsTr('emailsInput')
readOnly: !_edition
title: qsTr('emails')
onChanged: default_value.length === 0
? _vcard.addEmail(new_value)
: _vcard.updateEmail(default_value, new_value)
onChanged: defaultValue.length === 0
? _vcard.addEmail(newValue)
: _vcard.updateEmail(defaultValue, newValue)
onRemoved: _vcard.removeEmail(value)
}
@ -266,9 +310,10 @@ ColumnLayout {
defaultData: _vcard.urls
inputMethodHints: Qt.ImhUrlCharactersOnly
placeholder: qsTr('webSitesInput')
readOnly: !_edition
title: qsTr('webSites')
onChanged: _handleUrlChanged(index, default_value, new_value)
onChanged: _handleUrlChanged(index, defaultValue, newValue)
onRemoved: _vcard.removeUrl(value)
}
@ -278,20 +323,21 @@ ColumnLayout {
color: ContactEditStyle.values.separator.color
}
Loader {
Row {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: ContactEditStyle.buttons.topMargin
spacing: ContactEditStyle.buttons.spacing
visible: _edition
sourceComponent: Row {
spacing: ContactEditStyle.buttons.spacing
TextButtonB {
enabled: _vcard.sipAddresses.length > 0
text: qsTr('save')
onClicked: _save()
}
TextButtonB {
text: qsTr('save')
}
TextButtonA {
text: qsTr('cancel')
}
TextButtonA {
text: qsTr('cancel')
onClicked: _cancel()
}
}