vCard #29

Manually merged
blue merged 20 commits from vCard into master 2019-11-08 07:47:38 +00:00
10 changed files with 413 additions and 3 deletions
Showing only changes of commit 2c13f0d77c - Show all commits

View File

@ -218,6 +218,7 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) {
itr = phones.emplace(mPh.number, QXmppVCardPhone()).first; itr = phones.emplace(mPh.number, QXmppVCardPhone()).first;
} }
QXmppVCardPhone& phone = itr->second; QXmppVCardPhone& phone = itr->second;
phone.setNumber(mPh.number);
switch (mPh.type) { switch (mPh.type) {
case Shared::VCard::Phone::fax: case Shared::VCard::Phone::fax:

View File

@ -569,6 +569,7 @@ const std::deque<Shared::VCard::Phone> & Shared::VCard::getPhones() const
} }
const std::deque<QString>Shared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"}; const std::deque<QString>Shared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"};
const std::deque<QString>Shared::VCard::Phone::typeNames = {"Fax", "Pager", "Voice", "Cell", "Video", "Modem", "Other"};
QIcon Shared::availabilityIcon(Shared::Availability av, bool big) QIcon Shared::availabilityIcon(Shared::Availability av, bool big)
{ {

View File

@ -248,6 +248,7 @@ public:
modem, modem,
other other
}; };
static const std::deque<QString> typeNames;
Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false); Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false);
QString number; QString number;

View File

@ -12,6 +12,7 @@ find_package(Qt5Widgets CONFIG REQUIRED)
set(vCardUI_SRC set(vCardUI_SRC
vcard.cpp vcard.cpp
emailsmodel.cpp emailsmodel.cpp
phonesmodel.cpp
) )
# Tell CMake to create the helloworld executable # Tell CMake to create the helloworld executable

View File

@ -198,3 +198,8 @@ void UI::VCard::EMailsModel::revertPreferred(int row)
{ {
setData(createIndex(row, 2), !isPreferred(row)); setData(createIndex(row, 2), !isPreferred(row));
} }
QString UI::VCard::EMailsModel::getEmail(int row) const
{
return deque[row].address;
}

View File

@ -45,6 +45,7 @@ public:
void removeLines(int index, int count); void removeLines(int index, int count);
void setEmails(const std::deque<Shared::VCard::Email>& emails); void setEmails(const std::deque<Shared::VCard::Email>& emails);
void getEmails(std::deque<Shared::VCard::Email>& emails) const; void getEmails(std::deque<Shared::VCard::Email>& emails) const;
QString getEmail(int row) const;
public slots: public slots:
QModelIndex addNewEmptyLine(); QModelIndex addNewEmptyLine();

View File

@ -0,0 +1,222 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* 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 "phonesmodel.h"
UI::VCard::PhonesModel::PhonesModel(bool p_edit, QObject* parent):
QAbstractTableModel(parent),
edit(p_edit),
deque()
{
}
int UI::VCard::PhonesModel::columnCount(const QModelIndex& parent) const
{
return 4;
}
int UI::VCard::PhonesModel::rowCount(const QModelIndex& parent) const
{
return deque.size();
}
QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const
{
if (index.isValid()) {
int col = index.column();
switch (col) {
case 0:
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
return deque[index.row()].number;
default:
return QVariant();
}
break;
case 1:
switch (role) {
case Qt::DisplayRole:
return tr(Shared::VCard::Phone::roleNames[deque[index.row()].role].toStdString().c_str());
case Qt::EditRole:
return deque[index.row()].role;
default:
return QVariant();
}
break;
case 2:
switch (role) {
case Qt::DisplayRole:
return tr(Shared::VCard::Phone::typeNames[deque[index.row()].type].toStdString().c_str());
case Qt::EditRole:
return deque[index.row()].type;
default:
return QVariant();
}
break;
case 3:
switch (role) {
case Qt::DisplayRole:
return QVariant();
case Qt::DecorationRole:
if (deque[index.row()].prefered) {
return Shared::icon("favorite", false);
}
return QVariant();
default:
return QVariant();
}
break;
default:
return QVariant();
}
}
return QVariant();
}
QModelIndex UI::VCard::PhonesModel::addNewEmptyLine()
{
beginInsertRows(QModelIndex(), deque.size(), deque.size());
deque.emplace_back("", Shared::VCard::Phone::other);
endInsertRows();
return createIndex(deque.size() - 1, 0, &(deque.back()));
}
Qt::ItemFlags UI::VCard::PhonesModel::flags(const QModelIndex& index) const
{
Qt::ItemFlags f = QAbstractTableModel::flags(index);
if (edit && index.column() != 3) {
f = Qt::ItemIsEditable | f;
}
return f;
}
bool UI::VCard::PhonesModel::dropPrefered()
{
bool dropped = false;
int i = 0;
for (Shared::VCard::Phone& phone : deque) {
if (phone.prefered) {
phone.prefered = false;
QModelIndex ci = createIndex(i, 2, &phone);
emit dataChanged(ci, ci);
dropped = true;
}
++i;
}
return dropped;
}
void UI::VCard::PhonesModel::getPhones(std::deque<Shared::VCard::Phone>& phones) const
{
for (const Shared::VCard::Phone& my : deque) {
phones.emplace_back(my);
}
}
bool UI::VCard::PhonesModel::isPreferred(int row) const
{
if (row < deque.size()) {
return deque[row].prefered;
} else {
return false;
}
}
void UI::VCard::PhonesModel::removeLines(int index, int count)
{
if (index < deque.size()) {
int maxCount = deque.size() - index;
if (count > maxCount) {
count = maxCount;
}
if (count > 0) {
beginRemoveRows(QModelIndex(), index, index + count - 1);
std::deque<Shared::VCard::Phone>::const_iterator itr = deque.begin() + index;
std::deque<Shared::VCard::Phone>::const_iterator end = itr + count;
deque.erase(itr, end);
endRemoveRows();
}
}
}
void UI::VCard::PhonesModel::revertPreferred(int row)
{
setData(createIndex(row, 3), !isPreferred(row));
}
bool UI::VCard::PhonesModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (role == Qt::EditRole && checkIndex(index)) {
Shared::VCard::Phone& item = deque[index.row()];
switch (index.column()) {
case 0:
item.number = value.toString();
return true;
case 1: {
quint8 newRole = value.toUInt();
if (newRole > Shared::VCard::Phone::work) {
return false;
}
item.role = static_cast<Shared::VCard::Phone::Role>(newRole);
return true;
}
case 2: {
quint8 newType = value.toUInt();
if (newType > Shared::VCard::Phone::other) {
return false;
}
item.type = static_cast<Shared::VCard::Phone::Type>(newType);
return true;
}
case 3: {
bool newDef = value.toBool();
if (newDef != item.prefered) {
if (newDef) {
//dropPrefered();
}
item.prefered = newDef;
return true;
}
}
}
return true;
}
return false;
}
void UI::VCard::PhonesModel::setPhones(const std::deque<Shared::VCard::Phone>& phones)
{
if (deque.size() > 0) {
removeLines(0, deque.size());
}
if (phones.size() > 0) {
beginInsertRows(QModelIndex(), 0, phones.size() - 1);
for (const Shared::VCard::Phone& comming : phones) {
deque.emplace_back(comming);
}
endInsertRows();
}
}
QString UI::VCard::PhonesModel::getPhone(int row) const
{
return deque[row].number;
}

View File

@ -0,0 +1,65 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* 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 UI_VCARD_PHONESMODEL_H
#define UI_VCARD_PHONESMODEL_H
#include <QAbstractTableModel>
#include <QIcon>
#include "global.h"
namespace UI {
namespace VCard {
/**
* @todo write docs
*/
class PhonesModel : public QAbstractTableModel
{
Q_OBJECT
public:
PhonesModel(bool edit = false, QObject *parent = nullptr);
QVariant data(const QModelIndex& index, int role) const override;
int columnCount(const QModelIndex& parent) const override;
int rowCount(const QModelIndex& parent) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool isPreferred(int row) const;
void removeLines(int index, int count);
void setPhones(const std::deque<Shared::VCard::Phone>& phones);
void getPhones(std::deque<Shared::VCard::Phone>& phones) const;
QString getPhone(int row) const;
public slots:
QModelIndex addNewEmptyLine();
void revertPreferred(int row);
private:
bool edit;
std::deque<Shared::VCard::Phone> deque;
private:
bool dropPrefered();
};
}}
#endif // UI_VCARD_PHONESMODEL_H

View File

@ -38,7 +38,9 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent):
overlay(new QWidget()), overlay(new QWidget()),
contextMenu(new QMenu()), contextMenu(new QMenu()),
emails(edit), emails(edit),
roleDelegate(new ComboboxDelegate()) phones(edit),
roleDelegate(new ComboboxDelegate()),
phoneTypeDelegate(new ComboboxDelegate())
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->jabberID->setText(jid); m_ui->jabberID->setText(jid);
@ -57,13 +59,30 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent):
roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[1].toStdString().c_str())); roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[1].toStdString().c_str()));
roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[2].toStdString().c_str())); roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[2].toStdString().c_str()));
phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[0].toStdString().c_str()));
phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[1].toStdString().c_str()));
phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[2].toStdString().c_str()));
phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[3].toStdString().c_str()));
phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[4].toStdString().c_str()));
phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[5].toStdString().c_str()));
phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[6].toStdString().c_str()));
m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu);
m_ui->emailsView->setModel(&emails); m_ui->emailsView->setModel(&emails);
m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate);
m_ui->emailsView->setColumnWidth(2, 30); m_ui->emailsView->setColumnWidth(2, 25);
m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); m_ui->emailsView->horizontalHeader()->setStretchLastSection(false);
m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu);
m_ui->phonesView->setModel(&phones);
m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate);
m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate);
m_ui->phonesView->setColumnWidth(3, 25);
m_ui->phonesView->horizontalHeader()->setStretchLastSection(false);
m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu);
connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu);
if (edit) { if (edit) {
@ -121,6 +140,7 @@ VCard::~VCard()
avatarMenu->deleteLater(); avatarMenu->deleteLater();
} }
phoneTypeDelegate->deleteLater();
roleDelegate->deleteLater(); roleDelegate->deleteLater();
contextMenu->deleteLater(); contextMenu->deleteLater();
} }
@ -154,7 +174,9 @@ void VCard::setVCard(const Shared::VCard& card)
updateAvatar(); updateAvatar();
const std::deque<Shared::VCard::Email>& ems = card.getEmails(); const std::deque<Shared::VCard::Email>& ems = card.getEmails();
const std::deque<Shared::VCard::Phone>& phs = card.getPhones();
emails.setEmails(ems); emails.setEmails(ems);
phones.setPhones(phs);
} }
QString VCard::getJid() const QString VCard::getJid() const
@ -181,6 +203,7 @@ void VCard::onButtonBoxAccepted()
card.setAvatarType(currentAvatarType); card.setAvatarType(currentAvatarType);
emails.getEmails(card.getEmails()); emails.getEmails(card.getEmails());
phones.getPhones(card.getPhones());
emit saveVCard(card); emit saveVCard(card);
} }
@ -282,8 +305,8 @@ void VCard::onContextMenu(const QPoint& point)
bool hasMenu = false; bool hasMenu = false;
QAbstractItemView* snd = static_cast<QAbstractItemView*>(sender()); QAbstractItemView* snd = static_cast<QAbstractItemView*>(sender());
if (snd == m_ui->emailsView) { if (snd == m_ui->emailsView) {
hasMenu = true;
if (editable) { if (editable) {
hasMenu = true;
QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address"));
connect(add, &QAction::triggered, this, &VCard::onAddEmail); connect(add, &QAction::triggered, this, &VCard::onAddEmail);
@ -306,6 +329,37 @@ void VCard::onContextMenu(const QPoint& point)
connect(del, &QAction::triggered, this, &VCard::onRemoveEmail); connect(del, &QAction::triggered, this, &VCard::onRemoveEmail);
} }
} }
QAction* cp = contextMenu->addAction(Shared::icon("copy"), tr("Copy selected emails to clipboard"));
connect(cp, &QAction::triggered, this, &VCard::onCopyEmail);
} else if (snd == m_ui->phonesView) {
hasMenu = true;
if (editable) {
QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add phone number"));
connect(add, &QAction::triggered, this, &VCard::onAddPhone);
QItemSelectionModel* sm = m_ui->phonesView->selectionModel();
int selectionSize = sm->selectedRows().size();
if (selectionSize > 0) {
if (selectionSize == 1) {
int row = sm->selectedRows().at(0).row();
if (phones.isPreferred(row)) {
QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred"));
connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row));
} else {
QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred"));
connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row));
}
}
QAction* del = contextMenu->addAction(Shared::icon("remove"), tr("Remove selected phone numbers"));
connect(del, &QAction::triggered, this, &VCard::onRemovePhone);
}
}
QAction* cp = contextMenu->addAction(Shared::icon("copy"), tr("Copy selected phones to clipboard"));
connect(cp, &QAction::triggered, this, &VCard::onCopyPhone);
} }
if (hasMenu) { if (hasMenu) {
@ -326,6 +380,9 @@ void VCard::onAddAddress()
} }
void VCard::onAddPhone() void VCard::onAddPhone()
{ {
QModelIndex index = phones.addNewEmptyLine();
m_ui->phonesView->setCurrentIndex(index);
m_ui->phonesView->edit(index);
} }
void VCard::onRemoveAddress() void VCard::onRemoveAddress()
{ {
@ -353,4 +410,53 @@ void VCard::onRemoveEmail()
void VCard::onRemovePhone() void VCard::onRemovePhone()
{ {
QItemSelection selection(m_ui->phonesView->selectionModel()->selection());
QList<int> rows;
for (const QModelIndex& index : selection.indexes()) {
rows.append(index.row());
}
std::sort(rows.begin(), rows.end());
int prev = -1;
for (int i = rows.count() - 1; i >= 0; i -= 1) {
int current = rows[i];
if (current != prev) {
phones.removeLines(current, 1);
prev = current;
}
}
}
void VCard::onCopyEmail()
{
QItemSelection selection(m_ui->emailsView->selectionModel()->selection());
QList<QString> addrs;
for (const QModelIndex& index : selection.indexes()) {
addrs.push_back(emails.getEmail(index.row()));
}
QString list = addrs.join("\n");
qDebug() << list;
QClipboard* cb = QApplication::clipboard();
cb->setText(list);
}
void VCard::onCopyPhone()
{
QItemSelection selection(m_ui->phonesView->selectionModel()->selection());
QList<QString> phs;
for (const QModelIndex& index : selection.indexes()) {
phs.push_back(phones.getPhone(index.row()));
}
QString list = phs.join("\n");
qDebug() << list;
QClipboard* cb = QApplication::clipboard();
cb->setText(list);
} }

View File

@ -31,11 +31,14 @@
#include <QGraphicsOpacityEffect> #include <QGraphicsOpacityEffect>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QMenu> #include <QMenu>
#include <QApplication>
#include <QClipboard>
#include <set> #include <set>
#include "global.h" #include "global.h"
#include "emailsmodel.h" #include "emailsmodel.h"
#include "phonesmodel.h"
#include "ui/utils/progress.h" #include "ui/utils/progress.h"
#include "ui/utils/comboboxdelegate.h" #include "ui/utils/comboboxdelegate.h"
@ -71,8 +74,10 @@ private slots:
void onAddAddress(); void onAddAddress();
void onRemoveAddress(); void onRemoveAddress();
void onAddEmail(); void onAddEmail();
void onCopyEmail();
void onRemoveEmail(); void onRemoveEmail();
void onAddPhone(); void onAddPhone();
void onCopyPhone();
void onRemovePhone(); void onRemovePhone();
void onContextMenu(const QPoint& point); void onContextMenu(const QPoint& point);
@ -88,7 +93,9 @@ private:
QWidget* overlay; QWidget* overlay;
QMenu* contextMenu; QMenu* contextMenu;
UI::VCard::EMailsModel emails; UI::VCard::EMailsModel emails;
UI::VCard::PhonesModel phones;
ComboboxDelegate* roleDelegate; ComboboxDelegate* roleDelegate;
ComboboxDelegate* phoneTypeDelegate;
static const std::set<QString> supportedTypes; static const std::set<QString> supportedTypes;