diff --git a/CMakeLists.txt b/CMakeLists.txt index 5010adf..f39d0e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) include(GNUInstallDirs) +include_directories(.) find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5LinguistTools) diff --git a/core/account.cpp b/core/account.cpp index f3e4156..ff8c334 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -36,6 +36,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& am(new QXmppMamManager()), mm(new QXmppMucManager()), bm(new QXmppBookmarkManager()), + rm(client.findExtension()), + vm(client.findExtension()), contacts(), conferences(), maxReconnectTimes(0), @@ -57,12 +59,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(&client, &QXmppClient::messageReceived, this, &Account::onMessageReceived); QObject::connect(&client, &QXmppClient::error, this, &Account::onClientError); - QXmppRosterManager& rm = client.rosterManager(); - - QObject::connect(&rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived); - QObject::connect(&rm, &QXmppRosterManager::itemAdded, this, &Account::onRosterItemAdded); - QObject::connect(&rm, &QXmppRosterManager::itemRemoved, this, &Account::onRosterItemRemoved); - QObject::connect(&rm, &QXmppRosterManager::itemChanged, this, &Account::onRosterItemChanged); + QObject::connect(rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived); + QObject::connect(rm, &QXmppRosterManager::itemAdded, this, &Account::onRosterItemAdded); + QObject::connect(rm, &QXmppRosterManager::itemRemoved, this, &Account::onRosterItemRemoved); + QObject::connect(rm, &QXmppRosterManager::itemChanged, this, &Account::onRosterItemChanged); //QObject::connect(&rm, &QXmppRosterManager::presenceChanged, this, &Account::onRosterPresenceChanged); client.addExtension(cm); @@ -82,8 +82,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(bm); QObject::connect(bm, &QXmppBookmarkManager::bookmarksReceived, this, &Account::bookmarksReceived); - QXmppVCardManager& vm = client.vCardManager(); - QObject::connect(&vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); + QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); @@ -240,11 +239,10 @@ QString Core::Account::getServer() const void Core::Account::onRosterReceived() { - client.vCardManager().requestClientVCard(); //TODO need to make sure server actually supports vCards + vm->requestClientVCard(); //TODO need to make sure server actually supports vCards ownVCardRequestInProgress = true; - QXmppRosterManager& rm = client.rosterManager(); - QStringList bj = rm.getRosterBareJids(); + QStringList bj = rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { const QString& jid = bj[i]; addedAccount(jid); @@ -264,8 +262,7 @@ void Core::Account::onRosterItemAdded(const QString& bareJid) addedAccount(bareJid); std::map::const_iterator itr = queuedContacts.find(bareJid); if (itr != queuedContacts.end()) { - QXmppRosterManager& rm = client.rosterManager(); - rm.subscribe(bareJid, itr->second); + rm->subscribe(bareJid, itr->second); queuedContacts.erase(itr); } } @@ -278,8 +275,7 @@ void Core::Account::onRosterItemChanged(const QString& bareJid) return; } Contact* contact = itr->second; - QXmppRosterManager& rm = client.rosterManager(); - QXmppRosterIq::Item re = rm.getRosterEntry(bareJid); + QXmppRosterIq::Item re = rm->getRosterEntry(bareJid); Shared::SubscriptionState state = castSubscriptionState(re.subscriptionType()); @@ -308,9 +304,8 @@ void Core::Account::onRosterItemRemoved(const QString& bareJid) void Core::Account::addedAccount(const QString& jid) { - QXmppRosterManager& rm = client.rosterManager(); std::map::const_iterator itr = contacts.find(jid); - QXmppRosterIq::Item re = rm.getRosterEntry(jid); + QXmppRosterIq::Item re = rm->getRosterEntry(jid); Contact* contact; bool newContact = false; if (itr == contacts.end()) { @@ -410,13 +405,13 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) break; case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any if (avatarType.size() > 0) { - client.vCardManager().requestClientVCard(); + vm->requestClientVCard(); ownVCardRequestInProgress = true; } break; case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load if (avatarHash != p_presence.photoHash()) { - client.vCardManager().requestClientVCard(); + vm->requestClientVCard(); ownVCardRequestInProgress = true; } break; @@ -493,7 +488,7 @@ void Core::Account::onRosterPresenceChanged(const QString& bareJid, const QStrin { //not used for now; qDebug() << "presence changed for " << bareJid << " resource " << resource; - const QXmppPresence& presence = client.rosterManager().getPresence(bareJid, resource); + const QXmppPresence& presence = rm->getPresence(bareJid, resource); } void Core::Account::setLogin(const QString& p_login) @@ -1053,8 +1048,7 @@ void Core::Account::onClientError(QXmppClient::Error err) void Core::Account::subscribeToContact(const QString& jid, const QString& reason) { if (state == Shared::connected) { - QXmppRosterManager& rm = client.rosterManager(); - rm.subscribe(jid, reason); + rm->subscribe(jid, reason); } else { qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping"; } @@ -1063,8 +1057,7 @@ void Core::Account::subscribeToContact(const QString& jid, const QString& reason void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason) { if (state == Shared::connected) { - QXmppRosterManager& rm = client.rosterManager(); - rm.unsubscribe(jid, reason); + rm->unsubscribe(jid, reason); } else { qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping"; } @@ -1078,8 +1071,7 @@ void Core::Account::removeContactRequest(const QString& jid) outOfRosterContacts.erase(itr); onRosterItemRemoved(jid); } else { - QXmppRosterManager& rm = client.rosterManager(); - rm.removeItem(jid); + rm->removeItem(jid); } } else { qDebug() << "An attempt to remove contact " << jid << " from account " << name << " but the account is not in the connected state, skipping"; @@ -1095,8 +1087,7 @@ void Core::Account::addContactRequest(const QString& jid, const QString& name, c qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is already queued for adding, skipping"; } else { queuedContacts.insert(std::make_pair(jid, "")); //TODO need to add reason here; - QXmppRosterManager& rm = client.rosterManager(); - rm.addItem(jid, name, groups); + rm->addItem(jid, name, groups); } } else { qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is not in the connected state, skipping"; @@ -1273,8 +1264,7 @@ void Core::Account::addContactToGroupRequest(const QString& jid, const QString& if (itr == contacts.end()) { qDebug() << "An attempt to add non existing contact" << jid << "of account" << name << "to the group" << groupName << ", skipping"; } else { - QXmppRosterManager& rm = client.rosterManager(); - QXmppRosterIq::Item item = rm.getRosterEntry(jid); + QXmppRosterIq::Item item = rm->getRosterEntry(jid); QSet groups = item.groups(); if (groups.find(groupName) == groups.end()) { //TODO need to change it, I guess that sort of code is better in qxmpp lib groups.insert(groupName); @@ -1296,8 +1286,7 @@ void Core::Account::removeContactFromGroupRequest(const QString& jid, const QStr if (itr == contacts.end()) { qDebug() << "An attempt to remove non existing contact" << jid << "of account" << name << "from the group" << groupName << ", skipping"; } else { - QXmppRosterManager& rm = client.rosterManager(); - QXmppRosterIq::Item item = rm.getRosterEntry(jid); + QXmppRosterIq::Item item = rm->getRosterEntry(jid); QSet groups = item.groups(); QSet::const_iterator gItr = groups.find(groupName); if (gItr != groups.end()) { @@ -1320,8 +1309,7 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN if (itr == contacts.end()) { qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping"; } else { - QXmppRosterManager& rm = client.rosterManager(); - rm.renameItem(jid, newName); + rm->renameItem(jid, newName); } } @@ -1519,11 +1507,11 @@ void Core::Account::requestVCard(const QString& jid) if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { if (jid == getLogin() + "@" + getServer()) { if (!ownVCardRequestInProgress) { - client.vCardManager().requestClientVCard(); + vm->requestClientVCard(); ownVCardRequestInProgress = true; } } else { - client.vCardManager().requestVCard(jid); + vm->requestVCard(jid); pendingVCardRequests.insert(jid); } } @@ -1596,6 +1584,6 @@ void Core::Account::uploadVCard(const Shared::VCard& card) } } - client.vCardManager().setClientVCard(iq); + vm->setClientVCard(iq); onOwnVCardReceived(iq); } diff --git a/core/account.h b/core/account.h index 371a561..d6444d3 100644 --- a/core/account.h +++ b/core/account.h @@ -125,6 +125,8 @@ private: QXmppMamManager* am; QXmppMucManager* mm; QXmppBookmarkManager* bm; + QXmppRosterManager* rm; + QXmppVCardManager* vm; std::map contacts; std::map conferences; unsigned int maxReconnectTimes; diff --git a/global.cpp b/global.cpp index 9beb7a1..e3ffb11 100644 --- a/global.cpp +++ b/global.cpp @@ -538,6 +538,8 @@ QDateTime Shared::VCard::getReceivingTime() const return receivingTime; } +const std::dequeShared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"}; + QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { const std::deque& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ? diff --git a/global.h b/global.h index b046efa..c33bd38 100644 --- a/global.h +++ b/global.h @@ -223,6 +223,7 @@ class VCard { home, work }; + static const std::deque roleNames; Contact(Role p_role = none, bool p_prefered = false); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 59a0a4f..50d5304 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -10,6 +10,8 @@ set(CMAKE_AUTOUIC ON) find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5DBus CONFIG REQUIRED) +add_subdirectory(widgets) + set(squawkUI_SRC squawk.cpp models/accounts.cpp @@ -22,14 +24,6 @@ set(squawkUI_SRC models/room.cpp models/abstractparticipant.cpp models/participant.cpp - widgets/conversation.cpp - widgets/chat.cpp - widgets/room.cpp - widgets/newcontact.cpp - widgets/accounts.cpp - widgets/account.cpp - widgets/joinconference.cpp - widgets/vcard.cpp utils/messageline.cpp utils//message.cpp utils/resizer.cpp @@ -37,11 +31,13 @@ set(squawkUI_SRC utils/flowlayout.cpp utils/badge.cpp utils/progress.cpp + utils/comboboxdelegate.cpp ) # Tell CMake to create the helloworld executable add_library(squawkUI ${squawkUI_SRC}) # Use the Widgets module from Qt 5. +target_link_libraries(squawkUI squawkWidgets) target_link_libraries(squawkUI Qt5::Widgets) target_link_libraries(squawkUI Qt5::DBus) diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 5ddd525..0c27fc6 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -47,14 +47,14 @@ Squawk::Squawk(QWidget *parent) : } m_ui->comboBox->setCurrentIndex(Shared::offline); - connect(m_ui->actionAccounts, SIGNAL(triggered()), this, SLOT(onAccounts())); - connect(m_ui->actionAddContact, SIGNAL(triggered()), this, SLOT(onNewContact())); - connect(m_ui->actionAddConference, SIGNAL(triggered()), this, SLOT(onNewConference())); - connect(m_ui->comboBox, SIGNAL(activated(int)), this, SLOT(onComboboxActivated(int))); - connect(m_ui->roster, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onRosterItemDoubleClicked(const QModelIndex&))); - connect(m_ui->roster, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onRosterContextMenu(const QPoint&))); + connect(m_ui->actionAccounts, &QAction::triggered, this, &Squawk::onAccounts); + connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact); + connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference); + connect(m_ui->comboBox, qOverload(&QComboBox::activated), this, &Squawk::onComboboxActivated); + connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked); + connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu); - connect(rosterModel.accountsModel, SIGNAL(sizeChanged(unsigned int)), this, SLOT(onAccountsSizeChanged(unsigned int))); + connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); //m_ui->mainToolBar->addWidget(m_ui->comboBox); setWindowTitle(tr("Contact list")); diff --git a/ui/squawk.h b/ui/squawk.h index bf6582f..819d255 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -33,10 +33,10 @@ #include "widgets/room.h" #include "widgets/newcontact.h" #include "widgets/joinconference.h" -#include "widgets/vcard.h" #include "models/roster.h" +#include "widgets/vcard/vcard.h" -#include "../global.h" +#include "global.h" namespace Ui { class Squawk; diff --git a/ui/utils/comboboxdelegate.cpp b/ui/utils/comboboxdelegate.cpp new file mode 100644 index 0000000..824d1cf --- /dev/null +++ b/ui/utils/comboboxdelegate.cpp @@ -0,0 +1,64 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "comboboxdelegate.h" + +ComboboxDelegate::ComboboxDelegate(QObject *parent): + QStyledItemDelegate(parent), + entries() +{ +} + + +ComboboxDelegate::~ComboboxDelegate() +{ +} + + +QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QComboBox *cb = new QComboBox(parent); + + for (const std::pair pair : entries) { + cb->addItem(pair.second, pair.first); + } + + return cb; +} + + +void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + QComboBox *cb = static_cast(editor); + int currentIndex = index.data(Qt::EditRole).toInt(); + if (currentIndex >= 0) { + cb->setCurrentIndex(currentIndex); + } +} + + +void ComboboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); +} + +void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon) +{ + entries.emplace_back(title, icon); +} diff --git a/ui/utils/comboboxdelegate.h b/ui/utils/comboboxdelegate.h new file mode 100644 index 0000000..1d23a5c --- /dev/null +++ b/ui/utils/comboboxdelegate.h @@ -0,0 +1,47 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef COMBOBOXDELEGATE_H +#define COMBOBOXDELEGATE_H + +#include +#include + +#include + +/** + * @todo write docs + */ +class ComboboxDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + ComboboxDelegate(QObject *parent = nullptr); + ~ComboboxDelegate(); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + + void addEntry(const QString& title, const QIcon& icon = QIcon()); + +private: + std::deque> entries; +}; + +#endif // COMBOBOXDELEGATE_H diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt new file mode 100644 index 0000000..29e2dbc --- /dev/null +++ b/ui/widgets/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0) +project(squawkWidgets) + +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) +# Instruct CMake to create code from Qt designer ui files +set(CMAKE_AUTOUIC ON) + +# Find the QtWidgets library +find_package(Qt5Widgets CONFIG REQUIRED) + +add_subdirectory(vcard) + +set(squawkWidgets_SRC + conversation.cpp + chat.cpp + room.cpp + newcontact.cpp + accounts.cpp + account.cpp + joinconference.cpp +) + +# Tell CMake to create the helloworld executable +add_library(squawkWidgets ${squawkWidgets_SRC}) + +# Use the Widgets module from Qt 5. +target_link_libraries(squawkWidgets vCardUI) +target_link_libraries(squawkWidgets Qt5::Widgets) diff --git a/ui/widgets/vcard/CMakeLists.txt b/ui/widgets/vcard/CMakeLists.txt new file mode 100644 index 0000000..4af3d68 --- /dev/null +++ b/ui/widgets/vcard/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.0) +project(vCardUI) + +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) +# Instruct CMake to create code from Qt designer ui files +set(CMAKE_AUTOUIC ON) + +# Find the QtWidgets library +find_package(Qt5Widgets CONFIG REQUIRED) + +set(vCardUI_SRC + vcard.cpp + emailsmodel.cpp +) + +# Tell CMake to create the helloworld executable +add_library(vCardUI ${vCardUI_SRC}) + +# Use the Widgets module from Qt 5. +target_link_libraries(vCardUI Qt5::Widgets) diff --git a/ui/widgets/vcard/emailsmodel.cpp b/ui/widgets/vcard/emailsmodel.cpp new file mode 100644 index 0000000..4a5024f --- /dev/null +++ b/ui/widgets/vcard/emailsmodel.cpp @@ -0,0 +1,145 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "emailsmodel.h" + +UI::VCard::EMailsModel::EMailsModel(bool p_edit, QObject* parent): + QAbstractTableModel(parent), + edit(p_edit), + deque() +{ +} + +int UI::VCard::EMailsModel::columnCount(const QModelIndex& parent) const +{ + return 3; +} + +int UI::VCard::EMailsModel::rowCount(const QModelIndex& parent) const +{ + return deque.size(); +} + +QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const +{ + if (index.isValid()) { + int col = index.column(); + switch (col) { + case 0: + switch (role) { + case Qt::DisplayRole: + return deque[index.row()].address; + default: + return QVariant(); + } + break; + case 1: + switch (role) { + case Qt::DisplayRole: + return tr(Shared::VCard::Email::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 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(); +} + +Qt::ItemFlags UI::VCard::EMailsModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags f = QAbstractTableModel::flags(index); + if (edit && index.column() != 2) { + f = Qt::ItemIsEditable | f; + } + return f; +} + +bool UI::VCard::EMailsModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role == Qt::EditRole && checkIndex(index)) { + Shared::VCard::Email& item = deque[index.row()]; + switch (index.column()) { + case 0: + item.address = value.toString(); + return true; + case 1: { + quint8 newRole = value.toUInt(); + if (newRole > Shared::VCard::Email::work) { + return false; + } + item.role = static_cast(newRole); + return true; + } + case 2: { + bool newDef = value.toBool(); + if (newDef != item.prefered) { + if (newDef) { + dropPrefered(); + } + item.prefered = newDef; + return true; + } + } + } + return true; + } + return false; +} + + +bool UI::VCard::EMailsModel::dropPrefered() +{ + bool dropped = false; + int i = 0; + for (Shared::VCard::Email& email : deque) { + if (email.prefered) { + email.prefered = false; + QModelIndex ci = createIndex(i, 2, &email); + emit dataChanged(ci, ci); + dropped = true; + } + ++i; + } + return dropped; +} + +QModelIndex UI::VCard::EMailsModel::addNewEmptyLine() +{ + beginInsertRows(QModelIndex(), deque.size(), deque.size()); + deque.emplace_back(""); + endInsertRows(); + return createIndex(deque.size() - 1, 0, &(deque.back())); +} diff --git a/ui/widgets/vcard/emailsmodel.h b/ui/widgets/vcard/emailsmodel.h new file mode 100644 index 0000000..ddb0a57 --- /dev/null +++ b/ui/widgets/vcard/emailsmodel.h @@ -0,0 +1,57 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef UI_VCARD_EMAILSMODEL_H +#define UI_VCARD_EMAILSMODEL_H + +#include +#include + +#include + +#include "global.h" + +namespace UI { +namespace VCard { + +class EMailsModel : public QAbstractTableModel +{ + Q_OBJECT +public: + EMailsModel(bool edit = false, QObject *parent = nullptr); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + +public slots: + QModelIndex addNewEmptyLine(); + +private: + bool edit; + std::deque deque; + +private: + bool dropPrefered(); +}; + +}} + +#endif // UI_VCARD_EMAILSMODEL_H diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard/vcard.cpp similarity index 82% rename from ui/widgets/vcard.cpp rename to ui/widgets/vcard/vcard.cpp index b092ef8..3f7778e 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard/vcard.cpp @@ -33,7 +33,10 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): currentAvatarPath(""), progress(new Progress(100)), progressLabel(new QLabel()), - overlay(new QWidget()) + overlay(new QWidget()), + contextMenu(new QMenu()), + emails(edit), + roleDelegate(new ComboboxDelegate()) { m_ui->setupUi(this); m_ui->jabberID->setText(jid); @@ -48,6 +51,18 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): setAvatar->setEnabled(true); clearAvatar->setEnabled(false); + roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[0].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())); + + m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->emailsView->setModel(&emails); + m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); + m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); + m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); + if (edit) { avatarMenu = new QMenu(); m_ui->avatarButton->setMenu(avatarMenu); @@ -69,10 +84,6 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): m_ui->description->setReadOnly(true); m_ui->url->setReadOnly(true); m_ui->title->setText(tr("Contact %1 card").arg(jid)); - - m_ui->addAddressButton->hide(); - m_ui->addPhoneButton->hide(); - m_ui->addEmailButton->hide(); } connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); @@ -106,6 +117,9 @@ VCard::~VCard() if (editable) { avatarMenu->deleteLater(); } + + roleDelegate->deleteLater(); + contextMenu->deleteLater(); } void VCard::setVCard(const QString& jid, const Shared::VCard& card) @@ -253,3 +267,46 @@ void VCard::hideProgress() overlay->hide(); progress->stop(); } + +void VCard::onContextMenu(const QPoint& point) +{ + contextMenu->clear(); + bool hasMenu = false; + QAbstractItemView* snd = static_cast(sender()); + if (snd == m_ui->emailsView) { + if (editable) { + hasMenu = true; + QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); + connect(add, &QAction::triggered, this, &VCard::onAddEmail); + } + } + + if (hasMenu) { + contextMenu->popup(snd->viewport()->mapToGlobal(point)); + } +} + +void VCard::onAddEmail() +{ + QModelIndex index = emails.addNewEmptyLine(); + m_ui->emailsView->setCurrentIndex(index); + m_ui->emailsView->edit(index); +} + +void VCard::onAddAddress() +{ + +} +void VCard::onAddPhone() +{ +} +void VCard::onRemoveAddress() +{ +} +void VCard::onRemoveEmail() +{ +} + +void VCard::onRemovePhone() +{ +} diff --git a/ui/widgets/vcard.h b/ui/widgets/vcard/vcard.h similarity index 82% rename from ui/widgets/vcard.h rename to ui/widgets/vcard/vcard.h index 4831734..e150ae9 100644 --- a/ui/widgets/vcard.h +++ b/ui/widgets/vcard/vcard.h @@ -30,11 +30,14 @@ #include #include #include +#include #include -#include "../../global.h" -#include "../utils/progress.h" +#include "global.h" +#include "emailsmodel.h" +#include "ui/utils/progress.h" +#include "ui/utils/comboboxdelegate.h" namespace Ui { @@ -65,6 +68,13 @@ private slots: void onClearAvatar(); void onSetAvatar(); void onAvatarSelected(); + void onAddAddress(); + void onRemoveAddress(); + void onAddEmail(); + void onRemoveEmail(); + void onAddPhone(); + void onRemovePhone(); + void onContextMenu(const QPoint& point); private: QScopedPointer m_ui; @@ -76,6 +86,9 @@ private: Progress* progress; QLabel* progressLabel; QWidget* overlay; + QMenu* contextMenu; + UI::VCard::EMailsModel emails; + ComboboxDelegate* roleDelegate; static const std::set supportedTypes; diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard/vcard.ui similarity index 87% rename from ui/widgets/vcard.ui rename to ui/widgets/vcard/vcard.ui index fbd0b82..a4381b3 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard/vcard.ui @@ -9,8 +9,8 @@ 0 0 - 526 - 662 + 578 + 671 @@ -84,7 +84,7 @@ QTabWidget::Rounded - 0 + 1 Qt::ElideNone @@ -560,42 +560,72 @@ 0 0 - 514 - 488 + 566 + 497 - - - - - - 0 - 0 - - - - - - - + + + + + Qt::Horizontal - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - + + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + - + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + Qt::Vertical @@ -608,21 +638,43 @@ - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + - + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Qt::AlignHCenter|Qt::AlignTop @@ -681,124 +733,7 @@ - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - - - - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + Qt::Horizontal @@ -811,6 +746,32 @@ + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + @@ -905,7 +866,6 @@ jabberID url description - scrollArea