contact: implement add to contact feature
- Add right click on history item
- Copy number to clipboard
- Add to new Contact
- Add to existing contact
Issue: #77711
Issue: #77862
Issue: #77859
Change-Id: Ia249707e51c5208abbd67eeb3b04e6ca835bcd75
diff --git a/RingWinClient.pro b/RingWinClient.pro
index e5f5c63..42ef5bc 100644
--- a/RingWinClient.pro
+++ b/RingWinClient.pro
@@ -6,7 +6,7 @@
QT += core gui
-greaterThan(QT_MAJOR_VERSION, 4): QT += widgets svg
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets svg xml
VERSION = 0.3.0
GIT_VERSION = $$system(git --git-dir $$PWD/.git --work-tree $$PWD describe --always --tags)
@@ -47,7 +47,9 @@
accountstatedelegate.cpp \
videoview.cpp \
videooverlay.cpp \
- imdelegate.cpp
+ imdelegate.cpp \
+ contactdialog.cpp \
+ contactpicker.cpp
HEADERS += mainwindow.h \
callwidget.h \
@@ -70,7 +72,9 @@
accountstatedelegate.h \
videoview.h \
videooverlay.h \
- imdelegate.h
+ imdelegate.h \
+ contactdialog.h \
+ contactpicker.h
FORMS += mainwindow.ui \
callwidget.ui \
@@ -83,7 +87,9 @@
wizarddialog.ui \
instantmessagingwidget.ui \
videoview.ui \
- videooverlay.ui
+ videooverlay.ui \
+ contactdialog.ui \
+ contactpicker.ui
win32: LIBS += -lole32 -luuid -lshlwapi
@@ -118,7 +124,8 @@
RUNTIME.path = $$OUT_PWD/release
QTRUNTIME.files = $$RUNTIMEDIR/Qt5Core.dll $$RUNTIMEDIR/Qt5Widgets.dll \
- $$RUNTIMEDIR/Qt5Gui.dll $$RUNTIMEDIR/Qt5Svg.dll
+ $$RUNTIMEDIR/Qt5Gui.dll $$RUNTIMEDIR/Qt5Svg.dll \
+ $$RUNTIMEDIR/Qt5Xml.dll
QTRUNTIME.path = $$OUT_PWD/release
QTDEPSRUNTIME.files = $$RUNTIMEDIR/zlib1.dll $$RUNTIMEDIR/iconv.dll \
diff --git a/callwidget.cpp b/callwidget.cpp
index fa5eceb..670e632 100644
--- a/callwidget.cpp
+++ b/callwidget.cpp
@@ -19,10 +19,17 @@
#include "callwidget.h"
#include "ui_callwidget.h"
+#include <QClipboard>
+
#include <memory>
+//ERROR is defined in windows.h
+#include "utils.h"
+#undef ERROR
+
#include "audio/settings.h"
#include "personmodel.h"
+#include "person.h"
#include "fallbackpersoncollection.h"
#include "categorizedcontactmodel.h"
#include "localhistorycollection.h"
@@ -32,6 +39,8 @@
#include "wizarddialog.h"
#include "windowscontactbackend.h"
+#include "contactdialog.h"
+#include "contactpicker.h"
CallWidget::CallWidget(QWidget *parent) :
NavWidget(Main ,parent),
@@ -78,15 +87,18 @@
ui->callList->setModel(callModel_);
ui->callList->setSelectionModel(callModel_->selectionModel());
+ auto personCollection = PersonModel::instance()->
+ addCollection<WindowsContactBackend>(LoadOptions::FORCE_ENABLED);
+
+ CategorizedContactModel::instance()->setSortAlphabetical(false);
+ CategorizedContactModel::instance()->setUnreachableHidden(true);
+ ui->contactView->setModel(CategorizedContactModel::instance());
+ contactDelegate_ = new ContactDelegate();
+ ui->contactView->setItemDelegate(contactDelegate_);
+
CategorizedHistoryModel::instance()->
addCollection<LocalHistoryCollection>(LoadOptions::FORCE_ENABLED);
- PersonModel::instance()->
- addCollection<FallbackPersonCollection>(LoadOptions::FORCE_ENABLED);
-
- PersonModel::instance()->
- addCollection<WindowsContactBackend>(LoadOptions::FORCE_ENABLED);
-
ui->historyList->setModel(CategorizedHistoryModel::SortedProxy::instance()->model());
CategorizedHistoryModel::SortedProxy::instance()->model()->sort(0, Qt::DescendingOrder);
ui->historyList->setHeaderHidden(true);
@@ -99,14 +111,63 @@
ui->historyList->setExpanded(idx, true);
});
+ ui->historyList->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui->historyList, &QListView::customContextMenuRequested, [=](const QPoint& pos){
+ if (ui->historyList->currentIndex().parent().isValid()) {
+ QPoint globalPos = ui->historyList->mapToGlobal(pos);
+ QMenu menu;
+
+ ContactMethod* contactMethod = ui->historyList->currentIndex()
+ .data(static_cast<int>(Call::Role::ContactMethod)).value<ContactMethod*>();
+
+ auto copyAction = new QAction("Copy number", this);
+ menu.addAction(copyAction);
+ connect(copyAction, &QAction::triggered, [=]() {
+ QApplication::clipboard()->setText(contactMethod->uri());
+ });
+
+ if (not contactMethod->contact()) {
+ auto addNew = new QAction("Add to new contact", this);
+ menu.addAction(addNew);
+ connect(addNew, &QAction::triggered, [=]() {
+ ContactDialog dialog(contactMethod->uri());
+ auto ret = dialog.exec();
+ if (!ret || dialog.getName().isEmpty())
+ return;
+ auto *newPerson = new Person();
+ newPerson->setFormattedName(dialog.getName());
+ Person::ContactMethods cM;
+ cM.append(contactMethod);
+ newPerson->setContactMethods(cM);
+ newPerson->setUid(Utils::GenGUID().toLocal8Bit());
+ PersonModel::instance()->addNewPerson(newPerson, personCollection);
+ });
+ auto addExisting = new QAction("Add to existing contact", this);
+ menu.addAction(addExisting);
+ connect(addExisting, &QAction::triggered, [=]() {
+ /* Force LRC to update contact model as adding a number
+ to a contact without one didn't render him reachable */
+ CategorizedContactModel::instance()->setUnreachableHidden(false);
+
+ ContactPicker contactPicker;
+ contactPicker.move(globalPos.x(), globalPos.y() - (contactPicker.height()/2));
+ auto ret = contactPicker.exec();
+ if (!ret)
+ return;
+ auto p = contactPicker.getPersonSelected();
+ Person::ContactMethods cM (p->phoneNumbers());
+ cM.append(contactMethod);
+ p->setContactMethods(cM);
+ p->save();
+ CategorizedContactModel::instance()->setUnreachableHidden(true);
+ });
+ }
+ menu.exec(globalPos);
+ }
+ });
ui->sortComboBox->setModel(CategorizedHistoryModel::SortedProxy::instance()->categoryModel());
- CategorizedContactModel::instance()->setSortAlphabetical(false);
- ui->contactView->setModel(CategorizedContactModel::instance());
- contactDelegate_ = new ContactDelegate();
- ui->contactView->setItemDelegate(contactDelegate_);
-
findRingAccount();
} catch (...) {
@@ -250,7 +311,8 @@
}
void
-CallWidget::addedCall(Call* call, Call* parent) {
+CallWidget::addedCall(Call* call, Call* parent)
+{
Q_UNUSED(parent);
if (call->direction() == Call::Direction::OUTGOING) {
displaySpinner(true);
@@ -300,28 +362,32 @@
}
void
-CallWidget::atExit() {
+CallWidget::atExit()
+{
}
void
CallWidget::on_contactView_doubleClicked(const QModelIndex &index)
{
- QString uri;
+ if (not index.isValid())
+ return;
+
+ ContactMethod* uri;
auto var = index.child(0,0).data(
static_cast<int>(Person::Role::Object));
if (var.isValid()) {
Person* person = var.value<Person*>();
if (person->phoneNumbers().size() > 0) {
- uri = person->phoneNumbers().at(0)->uri();
+ uri = person->phoneNumbers().at(0); // FIXME: A person can have multiple contact method
+ if (uri) {
+ auto outCall = CallModel::instance()->dialingCall(person->formattedName());
+ outCall->setDialNumber(uri);
+ outCall->performAction(Call::Action::ACCEPT);
+ }
}
}
- if (not uri.isEmpty()) {
- auto outCall = CallModel::instance()->dialingCall(uri);
- outCall->setDialNumber(uri);
- outCall->performAction(Call::Action::ACCEPT);
- }
}
void
@@ -330,9 +396,9 @@
if (not index.isValid())
return;
- QString number = index.model()->data(index, static_cast<int>(Call::Role::Number)).toString();
- if (not number.isEmpty()) {
- auto outCall = CallModel::instance()->dialingCall(number);
+ auto number = index.data(static_cast<int>(Call::Role::ContactMethod)).value<ContactMethod*>();
+ if (number) {
+ auto outCall = CallModel::instance()->dialingCall();
outCall->setDialNumber(number);
outCall->performAction(Call::Action::ACCEPT);
}
diff --git a/contactdelegate.cpp b/contactdelegate.cpp
index e95b4fd..79c806f 100644
--- a/contactdelegate.cpp
+++ b/contactdelegate.cpp
@@ -35,7 +35,6 @@
initStyleOption(&opt, index);
if (index.column() == 0) {
- QString name = index.model()->data(index, Qt::DisplayRole).toString();
opt.text = "";
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
@@ -46,14 +45,13 @@
cg = QPalette::Inactive;
painter->setPen(opt.palette.color(cg, QPalette::Text));
painter->setOpacity(1.0);
- painter->drawText(QRect(rect.left()+sizeImage_+5, rect.top(),
- rect.width(), rect.height()/2),
- opt.displayAlignment, name);
-
QVariant var_c = index.child(0,0).data(
static_cast<int>(Person::Role::Object));
if (var_c.isValid()) {
Person *c = var_c.value<Person *>();
+ painter->drawText(QRect(rect.left()+sizeImage_+5, rect.top(),
+ rect.width(), rect.height()/2),
+ opt.displayAlignment, c->formattedName());
QVariant var_p = c->photo();
painter->drawRect(QRect(rect.left(), rect.top(),
sizeImage_+1, sizeImage_+1));
diff --git a/contactdialog.cpp b/contactdialog.cpp
new file mode 100644
index 0000000..fd96c24
--- /dev/null
+++ b/contactdialog.cpp
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Savoir-faire Linux *
+ * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ **************************************************************************/
+
+#include "contactdialog.h"
+#include "ui_contactdialog.h"
+
+ContactDialog::ContactDialog(const QString &number, QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::ContactDialog)
+{
+ ui->setupUi(this);
+ ui->numberLineEdit->setText(number);
+}
+
+ContactDialog::~ContactDialog()
+{
+ delete ui;
+}
+
+const QString&
+ContactDialog::getName()
+{
+ return contactName_;
+}
+
+void
+ContactDialog::on_nameLineEdit_textChanged(const QString &arg1)
+{
+ contactName_ = arg1;
+}
diff --git a/contactdialog.h b/contactdialog.h
new file mode 100644
index 0000000..b97cdb2
--- /dev/null
+++ b/contactdialog.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Savoir-faire Linux *
+ * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ **************************************************************************/
+
+#ifndef CONTACTDIALOG_H
+#define CONTACTDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+class ContactDialog;
+}
+
+class ContactDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ContactDialog(const QString& number, QWidget *parent = 0);
+ ~ContactDialog();
+
+ const QString &getName();
+
+private slots:
+ void on_nameLineEdit_textChanged(const QString &arg1);
+
+private:
+ Ui::ContactDialog *ui;
+ QString contactName_;
+};
+
+#endif // CONTACTDIALOG_H
diff --git a/contactdialog.ui b/contactdialog.ui
new file mode 100644
index 0000000..4d4e80f
--- /dev/null
+++ b/contactdialog.ui
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ContactDialog</class>
+ <widget class="QDialog" name="ContactDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>398</width>
+ <height>154</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>New Contact</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="nameLineEdit">
+ <property name="placeholderText">
+ <string>Enter a name...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="numberLineEdit">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ContactDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ContactDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/contactpicker.cpp b/contactpicker.cpp
new file mode 100644
index 0000000..417d30c
--- /dev/null
+++ b/contactpicker.cpp
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Savoir-faire Linux *
+ * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ **************************************************************************/
+
+#include "contactpicker.h"
+#include "ui_contactpicker.h"
+
+#include "categorizedcontactmodel.h"
+
+ContactPicker::ContactPicker(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::ContactPicker),
+ personSelected_(nullptr)
+{
+ ui->setupUi(this);
+
+ this->setWindowFlags(Qt::CustomizeWindowHint);
+ this->setWindowFlags(Qt::FramelessWindowHint);
+
+ auto personModel = PersonModel::instance();
+ ui->contactView->setModel(personModel);
+}
+
+ContactPicker::~ContactPicker()
+{
+ delete ui;
+}
+
+void
+ContactPicker::on_contactView_doubleClicked(const QModelIndex &index)
+{
+ personSelected_ = index.data(static_cast<int>(Person::Role::Object)).value<Person*>();
+ this->accept();
+}
+
+Person*
+ContactPicker::getPersonSelected()
+{
+ return personSelected_;
+}
+
+void
+ContactPicker::on_cancelButton_clicked()
+{
+ this->reject();
+}
diff --git a/contactpicker.h b/contactpicker.h
new file mode 100644
index 0000000..168d843
--- /dev/null
+++ b/contactpicker.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Savoir-faire Linux *
+ * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ **************************************************************************/
+
+#ifndef CONTACTPICKER_H
+#define CONTACTPICKER_H
+
+#include <QDialog>
+
+#include "personmodel.h"
+
+namespace Ui {
+class ContactPicker;
+}
+
+class ContactPicker : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ContactPicker(QWidget *parent = 0);
+ ~ContactPicker();
+
+ Person *getPersonSelected();
+
+//UI SLOTS
+private slots:
+ void on_contactView_doubleClicked(const QModelIndex &index);
+ void on_cancelButton_clicked();
+
+private:
+ Ui::ContactPicker *ui;
+ Person *personSelected_;
+};
+
+#endif // CONTACTPICKER_H
diff --git a/contactpicker.ui b/contactpicker.ui
new file mode 100644
index 0000000..b1cb6e3
--- /dev/null
+++ b/contactpicker.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ContactPicker</class>
+ <widget class="QDialog" name="ContactPicker">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>249</width>
+ <height>438</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QListView" name="contactView"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/utils.cpp b/utils.cpp
index 7e8a25b..18515cb 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -117,3 +117,33 @@
#endif
}
+QString
+Utils::GenGUID() {
+#ifdef Q_OS_WIN32
+ GUID gidReference;
+ wchar_t *str;
+ HRESULT hCreateGuid = CoCreateGuid(&gidReference);
+ if (hCreateGuid == S_OK) {
+ StringFromCLSID(gidReference, &str);
+ auto gStr = QString::fromWCharArray(str);
+ return gStr.remove("{").remove("}").toLower();
+ }
+ else
+ return QString("");
+#else
+ return QString("");
+#endif
+}
+
+QString
+Utils::GetISODate() {
+#ifdef Q_OS_WIN32
+ SYSTEMTIME lt;
+ GetSystemTime(<);
+ return QString("%1-%2-%3T%4:%5:%6Z").arg(lt.wYear).arg(lt.wMonth,2,10,QChar('0')).arg(lt.wDay,2,10,QChar('0'))
+ .arg(lt.wHour,2,10,QChar('0')).arg(lt.wMinute,2,10,QChar('0')).arg(lt.wSecond,2,10,QChar('0'));
+#else
+ return QString("");
+#endif
+}
+
diff --git a/utils.h b/utils.h
index 46cf54f..519a46e 100644
--- a/utils.h
+++ b/utils.h
@@ -39,6 +39,8 @@
static bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink);
static bool CheckStartupLink();
static QString GetRingtonePath();
+ static QString GenGUID();
+ static QString GetISODate();
};
diff --git a/windowscontactbackend.cpp b/windowscontactbackend.cpp
index f8adb26..f246e94 100644
--- a/windowscontactbackend.cpp
+++ b/windowscontactbackend.cpp
@@ -18,6 +18,13 @@
#include "windowscontactbackend.h"
+#include <QtXml>
+
+#include "personmodel.h"
+#include "categorizedcontactmodel.h"
+
+#include "utils.h"
+
WindowsContactEditor::WindowsContactEditor(CollectionMediator<Person> *m
, WindowsContactBackend *parent)
: CollectionEditor<Person>(m),collection_(parent)
@@ -33,15 +40,65 @@
bool
WindowsContactEditor::save(const Person *item)
{
- Q_UNUSED(item)
- return false;
+ QFile file(QStandardPaths::writableLocation
+ (QStandardPaths::HomeLocation) + "/Contacts/"
+ + item->formattedName() + ".contact");
+ if (!file.open(QIODevice::ReadWrite)) {
+ file.close();
+ qDebug() << "Cannot open contact file";
+ return false;
+ }
+
+ QDomDocument doc;
+ doc.setContent(&file);
+
+ auto root = doc.elementsByTagName("c:contact").at(0);
+ auto nodes = doc.elementsByTagName("c:PhoneNumberCollection");
+
+ //if PhoneNumberCollection already exists
+ QVector<QString> nodeNumberVector;
+ if (nodes.length()) {
+ auto phoneNumberCollection = nodes.at(0);
+ auto phoneNumbers = doc.elementsByTagName("c:Number");
+ auto virtualPhoneNumber = item->phoneNumbers();
+ for (int i = 0; i < phoneNumbers.length(); i++) {
+ auto node = phoneNumbers.at(i).toElement();
+ nodeNumberVector.append(node.text());
+ }
+ for (auto elem : virtualPhoneNumber) {
+ if (not nodeNumberVector.contains(elem->uri())) {
+ auto phoneNumber = doc.createElement("c:PhoneNumber");
+ phoneNumberCollection.appendChild(phoneNumber);
+ phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
+ auto numberNode = doc.createElement("c:Number");
+ phoneNumber.appendChild(numberNode);
+ auto numberValue = doc.createTextNode(elem->uri());
+ numberNode.appendChild(numberValue);
+ }
+ }
+ } else {
+ auto phoneNumberCollection = doc.createElement("c:PhoneNumberCollection");
+ root.appendChild(phoneNumberCollection);
+ auto phoneNumber = doc.createElement("c:PhoneNumber");
+ phoneNumberCollection.appendChild(phoneNumber);
+ phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
+ auto numberNode = doc.createElement("c:Number");
+ phoneNumber.appendChild(numberNode);
+ auto numberValue = doc.createTextNode(item->phoneNumbers().at(0)->uri());
+ numberNode.appendChild(numberValue);
+ }
+ file.resize(0);
+ file.write(doc.toByteArray());
+ file.close();
+ return true;
}
bool
WindowsContactEditor::remove(const Person *item)
{
- Q_UNUSED(item)
- return false;
+ items_.removeOne(const_cast<Person*>(item));
+ mediator()->removeItem(item);
+ return true;
}
bool
@@ -54,8 +111,82 @@
bool
WindowsContactEditor::addNew(const Person *item)
{
- Q_UNUSED(item)
- return false;
+ QDomDocument doc;
+ QFile file(QStandardPaths::writableLocation
+ (QStandardPaths::HomeLocation) + "/Contacts/"
+ + item->formattedName()+".contact");
+ if (file.exists())
+ return false;
+ if (!file.open(QIODevice::ReadWrite)) {
+ file.close();
+ qDebug() << "Cannot create contact file";
+ return false;
+ }
+ doc.appendChild(
+ doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""));
+
+ //Create root
+ auto root = doc.createElement("c:contact");
+ root.setAttribute("c:Version", "1");
+ root.setAttribute("xmlns:c", "http://schemas.microsoft.com/Contact");
+ root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ root.setAttribute("xmlns:MSP2P","http://schemas.microsoft.com/Contact/Extended/MSP2P");
+ doc.appendChild(root);
+
+ auto date = Utils::GetISODate();
+
+ //Create creation date
+ auto creationDateNode = doc.createElement("c:CreationDate");
+ auto creationDateValue = doc.createTextNode(date);
+ creationDateNode.appendChild(creationDateValue);
+ root.appendChild(creationDateNode);
+
+ //Create extended node
+ auto extendedNode = doc.createElement("c:Extended");
+ extendedNode.setAttribute("xsi:nil", "true");
+ root.appendChild(extendedNode);
+
+ //Create contactID collection
+ auto contactIDCol = doc.createElement("c:ContactIDCollection");
+ root.appendChild(contactIDCol);
+ auto contactID = doc.createElement("c:ContactID");
+ contactID.setAttribute("c:ElementID", Utils::GenGUID());
+ auto contactValue = doc.createElement("c:Value");
+ auto value = doc.createTextNode(item->uid());
+ contactValue.appendChild(value);
+ contactID.appendChild(contactValue);
+ contactIDCol.appendChild(contactID);
+
+ //Create NameCollection
+ auto nameCollection = doc.createElement("c:NameCollection");
+ root.appendChild(nameCollection);
+ auto name = doc.createElement("c:Name");
+ nameCollection.appendChild(name);
+ name.setAttribute("c:ElementID", Utils::GenGUID());
+ auto formattedName = doc.createElement("c:FormattedName");
+ name.appendChild(formattedName);
+ auto formattedNameValue = doc.createTextNode(item->formattedName());
+ formattedName.appendChild(formattedNameValue);
+
+ //Create PhoneNumberCollection
+ auto phoneNumberCollection = doc.createElement("c:PhoneNumberCollection");
+ root.appendChild(phoneNumberCollection);
+ auto phoneNumber = doc.createElement("c:PhoneNumber");
+ phoneNumberCollection.appendChild(phoneNumber);
+ phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
+ auto numberNode = doc.createElement("c:Number");
+ phoneNumber.appendChild(numberNode);
+ auto numberValue = doc.createTextNode(item->phoneNumbers().at(0)->uri());
+ numberNode.appendChild(numberValue);
+
+ //Write to file
+ file.write(doc.toByteArray());
+ file.close();
+
+ //Add it to the collection
+ addExisting(item);
+
+ return true;
}
bool
@@ -75,7 +206,7 @@
WindowsContactBackend::WindowsContactBackend(CollectionMediator<Person>* mediator,
CollectionInterface* parent)
: CollectionInterface(new WindowsContactEditor(mediator,this), parent)
- , mediator_(mediator)
+ , mediator_(mediator), watcher_(new QFileSystemWatcher())
{
}
@@ -89,6 +220,12 @@
WindowsContactBackend::load()
{
QtConcurrent::run(this, &WindowsContactBackend::loadRun);
+ watcher_->addPath(QStandardPaths::writableLocation
+ (QStandardPaths::HomeLocation) + "/Contacts");
+ QObject::connect(watcher_, &QFileSystemWatcher::directoryChanged, [=](QString path) {
+ Q_UNUSED(path)
+ QtConcurrent::run(this, &WindowsContactBackend::loadRun);
+ });
return true;
}
@@ -98,84 +235,115 @@
QDir contactDir(QStandardPaths::writableLocation
(QStandardPaths::HomeLocation) + "/Contacts");
QStringList filesList = contactDir.entryList(QStringList("*.contact"));
-
+ auto ret = true;
for(auto contactFileName : filesList) {
- QString contactFilePath
- (contactDir.absolutePath() + "/" + contactFileName);
- QFile contactFile(contactFilePath);
- if (contactFile.open(QIODevice::ReadOnly)) {
- QXmlStreamReader reader;
- Person *p = new Person();
- QVector<ContactMethod*> contactMethod;
- reader.setDevice(&contactFile);
- while (!reader.atEnd()) {
- reader.readNext();
- if (reader.isStartElement()) {
- QString name = reader.name().toString();
- if (name == "FormattedName")
- p->setFormattedName(reader.readElementText());
- else if (name == "NickName")
- p->setNickName(reader.readElementText());
- else if (name == "GivenName")
- p->setFirstName(reader.readElementText());
- else if (name == "FamilyName")
- p->setFamilyName(reader.readElementText());
- else if (name == "Company")
- p->setOrganization(reader.readElementText());
- else if (name == "Department")
- p->setDepartment(reader.readElementText());
- else if (name == "Number") {
- QString number = reader.readElementText();
- if (not number.isEmpty()) {
- ContactMethod *contact = PhoneDirectoryModel::instance()->getNumber(number);
- contactMethod.append(contact);
- }
+ if (not getPersonFromContactFile(contactDir, contactFileName))
+ ret = false;
+ }
+ return ret;
+}
+
+bool
+WindowsContactBackend::getPersonFromContactFile(const QDir& contactDir,
+ const QString &contactFileName)
+{
+ QString contactFilePath
+ (contactDir.absolutePath() + "/" + contactFileName);
+ QFile contactFile(contactFilePath);
+ if (contactFile.open(QIODevice::ReadOnly)) {
+ QXmlStreamReader reader;
+ Person *p = new Person(this);
+ QVector<ContactMethod*> contactMethod;
+ reader.setDevice(&contactFile);
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.isStartElement()) {
+ QString name = reader.name().toString();
+ if (name == "FormattedName")
+ p->setFormattedName(reader.readElementText());
+ else if (name == "NickName")
+ p->setNickName(reader.readElementText());
+ else if (name == "GivenName")
+ p->setFirstName(reader.readElementText());
+ else if (name == "FamilyName")
+ p->setFamilyName(reader.readElementText());
+ else if (name == "Company")
+ p->setOrganization(reader.readElementText());
+ else if (name == "Department")
+ p->setDepartment(reader.readElementText());
+ else if (name == "Number") {
+ QString number = reader.readElementText();
+ if (not number.isEmpty()) {
+ ContactMethod *contact =
+ PhoneDirectoryModel::instance()->getNumber(number,p);
+ contactMethod.append(contact);
}
- else if (name == "Photo") {
- //FIXME: It seems to be possible to have multiple photo...
+ } else if (name == "ContactID") {
+ while (reader.name().toString() != "Value")
reader.readNext();
- if (reader.name().toString() == "Url") {
- QString photoValue = reader.readElementText();
- QImage photo;
- photo.load(photoValue);
- p->setPhoto(photo.scaled(sizePhoto_,sizePhoto_, Qt::KeepAspectRatio,
- Qt::SmoothTransformation));
- }
- }
- else if (name == "EmailAddress") {
- QString address;
- bool isPreferred = false;
- reader.readNext();
- while (reader.name().toString() != "EmailAddress"
- && !reader.atEnd()) {
- if (reader.isStartElement()) {
- QString tag = reader.name().toString();
- if (tag == "Address")
- address = reader.readElementText();
- else if (tag == "Label")
- if (reader.readElementText() == "Preferred")
- isPreferred = true;
- }
- reader.readNext();
- }
- if (isPreferred)
- p->setPreferredEmail(address);
+ p->setUid(reader.readElementText().toUtf8());
+ }
+ else if (name == "Photo") {
+ //FIXME: It seems to be possible to have multiple photo...
+ reader.readNext();
+ if (reader.name().toString() == "Url") {
+ QString photoValue = reader.readElementText();
+ QImage photo;
+ photo.load(photoValue);
+ p->setPhoto(photo.scaled(sizePhoto_,sizePhoto_, Qt::KeepAspectRatio,
+ Qt::SmoothTransformation));
}
}
+ else if (name == "EmailAddress") {
+ QString address;
+ bool isPreferred = false;
+ reader.readNext();
+ while (reader.name().toString() != "EmailAddress"
+ && !reader.atEnd()) {
+ if (reader.isStartElement()) {
+ QString tag = reader.name().toString();
+ if (tag == "Address")
+ address = reader.readElementText();
+ else if (tag == "Label")
+ if (reader.readElementText() == "Preferred")
+ isPreferred = true;
+ }
+ reader.readNext();
+ }
+ if (isPreferred)
+ p->setPreferredEmail(address);
+ }
}
- if (reader.hasError()) {
- qDebug() << reader.errorString();
- } else {
+ }
+ if (reader.hasError()) {
+ qDebug() << reader.errorString();
+ contactFile.close();
+ return false;
+ } else {
+ Person* existing = PersonModel::instance()->getPersonByUid(p->uid());
+ if (existing) {
if (contactMethod.size() > 0)
- p->setContactMethods(contactMethod);
+ existing->setContactMethods ( contactMethod );
+ existing->setNickName ( p->nickName() );
+ existing->setFirstName ( p->firstName() );
+ existing->setFamilyName ( p->secondName() );
+ existing->setFormattedName ( p->formattedName() );
+ existing->setOrganization ( p->organization() );
+ existing->setPreferredEmail ( p->preferredEmail() );
+ existing->setGroup ( p->group() );
+ existing->setDepartment ( p->department() );
+ existing->setPhoto ( p->photo() );
+ delete p;
+ } else {
+ p->setContactMethods(contactMethod);
editor<Person>()->addExisting(p);
}
- } else {
- qDebug() << "Error Opening contact file : " << contactFileName;
+ return true;
}
+ } else {
+ qDebug() << "Error Opening contact file : " << contactFileName;
+ return false;
}
-
- return false;
}
bool
@@ -218,6 +386,9 @@
{
return (
CollectionInterface::SupportedFeatures::NONE |
- CollectionInterface::SupportedFeatures::LOAD);
+ CollectionInterface::SupportedFeatures::LOAD |
+ CollectionInterface::SupportedFeatures::SAVE |
+ CollectionInterface::SupportedFeatures::REMOVE |
+ CollectionInterface::SupportedFeatures::ADD);
}
diff --git a/windowscontactbackend.h b/windowscontactbackend.h
index c804a06..160b2ac 100644
--- a/windowscontactbackend.h
+++ b/windowscontactbackend.h
@@ -25,6 +25,7 @@
#include <QXmlStreamReader>
#include <QtConcurrent/QtConcurrent>
#include <QImage>
+#include <QFileSystemWatcher>
#include "person.h"
#include "collectioninterface.h"
@@ -51,6 +52,10 @@
private:
CollectionMediator<Person>* mediator_;
constexpr static int sizePhoto_ = 50;
+ QFileSystemWatcher* watcher_;
+
+private:
+ bool getPersonFromContactFile(const QDir &contactDir, const QString& contactFileName);
};
class WindowsContactEditor : public CollectionEditor<Person>