From edf1ee60cdb1ee7fcd3772118852b806106371b3 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 2 Feb 2023 21:39:38 +0300 Subject: [PATCH] keep going on refactoring vcard --- shared/enums.h | 10 ++ shared/info.cpp | 5 +- shared/info.h | 4 +- ui/widgets/info/CMakeLists.txt | 3 + ui/widgets/info/contactgeneral.cpp | 176 ++++++++++++++++++++++++++++- ui/widgets/info/contactgeneral.h | 36 ++++++ ui/widgets/info/contactgeneral.ui | 18 +++ ui/widgets/info/description.cpp | 27 +++++ ui/widgets/info/description.h | 41 +++++++ ui/widgets/info/description.ui | 48 ++++++++ ui/widgets/info/info.cpp | 81 ++++++++++++- ui/widgets/info/info.h | 25 +++- ui/widgets/info/info.ui | 64 ++++------- 13 files changed, 483 insertions(+), 55 deletions(-) create mode 100644 ui/widgets/info/description.cpp create mode 100644 ui/widgets/info/description.h create mode 100644 ui/widgets/info/description.ui diff --git a/shared/enums.h b/shared/enums.h index 9fb4aad..9eb9e5e 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -118,6 +118,16 @@ Q_ENUM_NS(AccountPassword) static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet; static const AccountPassword AccountPasswordLowest = AccountPassword::plain; +enum class EntryType { + contact, + conference, + presence, + participant +}; +Q_ENUM_NS(EntryType) +static const EntryType EntryTypeHighest = EntryType::participant; +static const EntryType EntryTypeLowest = EntryType::contact; + enum class Support { unknown, supported, diff --git a/shared/info.cpp b/shared/info.cpp index a5cf046..a0ac299 100644 --- a/shared/info.cpp +++ b/shared/info.cpp @@ -16,7 +16,8 @@ #include "info.h" -Shared::Info::Info(const QString& p_jid, bool p_editable): +Shared::Info::Info(const QString& p_jid, EntryType p_type, bool p_editable): + type(p_type), jid(p_jid), editable(p_editable), vcard(), @@ -25,6 +26,7 @@ Shared::Info::Info(const QString& p_jid, bool p_editable): {} Shared::Info::Info(): + type(EntryType::contact), jid(), editable(false), vcard(), @@ -33,6 +35,7 @@ Shared::Info::Info(): {} Shared::Info::Info(const Shared::Info& other): + type(other.type), jid(other.jid), editable(other.editable), vcard(other.vcard), diff --git a/shared/info.h b/shared/info.h index c3f16f8..90248c3 100644 --- a/shared/info.h +++ b/shared/info.h @@ -19,6 +19,7 @@ #include "vcard.h" #include "keyinfo.h" +#include "enums.h" #include @@ -33,10 +34,11 @@ namespace Shared { class Info { public: Info(); - Info(const QString& jid, bool editable = false); + Info(const QString& jid, EntryType = EntryType::contact, bool editable = false); Info(const Info& other); ~Info(); + EntryType type; QString jid; bool editable; VCard vcard; diff --git a/ui/widgets/info/CMakeLists.txt b/ui/widgets/info/CMakeLists.txt index 0029e84..44edb66 100644 --- a/ui/widgets/info/CMakeLists.txt +++ b/ui/widgets/info/CMakeLists.txt @@ -2,18 +2,21 @@ set(SOURCE_FILES info.cpp contactgeneral.cpp contactcontacts.cpp + description.cpp ) set(UI_FILES info.ui contactgeneral.ui contactcontacts.ui + description.ui ) set(HEADER_FILES info.h contactgeneral.h contactcontacts.h + description.h ) target_sources(squawk PRIVATE diff --git a/ui/widgets/info/contactgeneral.cpp b/ui/widgets/info/contactgeneral.cpp index 62efe81..57ce700 100644 --- a/ui/widgets/info/contactgeneral.cpp +++ b/ui/widgets/info/contactgeneral.cpp @@ -17,12 +17,182 @@ #include "contactgeneral.h" #include "ui_contactgeneral.h" +#include + +const std::set UI::ContactGeneral::supportedTypes = {"image/jpeg", "image/png"}; +constexpr int maxAvatarSize = 160; + UI::ContactGeneral::ContactGeneral(QWidget* parent): QWidget(parent), - m_ui(new Ui::ContactGeneral) + m_ui(new Ui::ContactGeneral), + avatarMenu(nullptr), + avatarButtonMargins(), + currentAvatarType(Shared::Avatar::empty), + currentAvatarPath(""), + currentJid(""), + editable(false), + avatarDiablog(nullptr) { m_ui->setupUi(this); + + initializeActions(); + initializeAvatar(); } -UI::ContactGeneral::~ContactGeneral() -{} +UI::ContactGeneral::~ContactGeneral() { + if (avatarMenu != nullptr) + avatarMenu->deleteLater(); + + if (avatarDiablog != nullptr) + deleteAvatarDialog(); +} + +QString UI::ContactGeneral::title() const { + return m_ui->generalHeading->text();} + +void UI::ContactGeneral::setEditable(bool edit) { + m_ui->fullName->setReadOnly(!edit); + m_ui->firstName->setReadOnly(!edit); + m_ui->middleName->setReadOnly(!edit); + m_ui->lastName->setReadOnly(!edit); + m_ui->nickName->setReadOnly(!edit); + m_ui->birthday->setReadOnly(!edit); + m_ui->organizationName->setReadOnly(!edit); + m_ui->organizationDepartment->setReadOnly(!edit); + m_ui->organizationTitle->setReadOnly(!edit); + m_ui->organizationRole->setReadOnly(!edit); + + if (edit) { + avatarMenu = new QMenu(); + m_ui->avatarButton->setMenu(avatarMenu); + avatarMenu->addAction(m_ui->actionSetAvatar); + avatarMenu->addAction(m_ui->actionClearAvatar); + } else { + if (avatarMenu != nullptr) { + avatarMenu->deleteLater(); + avatarMenu = nullptr; + m_ui->avatarButton->setMenu(nullptr); + } + } +} + +void UI::ContactGeneral::deleteAvatarDialog() { + avatarDiablog->deleteLater(); + + disconnect(avatarDiablog, &QFileDialog::accepted, this, &UI::ContactGeneral::avatarSelected); + disconnect(avatarDiablog, &QFileDialog::rejected, this, &UI::ContactGeneral::deleteAvatarDialog); + + avatarDiablog = nullptr; +} + +void UI::ContactGeneral::initializeActions() { + QAction* setAvatar = m_ui->actionSetAvatar; + QAction* clearAvatar = m_ui->actionClearAvatar; + + connect(setAvatar, &QAction::triggered, this, &UI::ContactGeneral::onSetAvatar); + connect(clearAvatar, &QAction::triggered, this, &UI::ContactGeneral::onClearAvatar); + + setAvatar->setEnabled(editable); + clearAvatar->setEnabled(false); +} + +void UI::ContactGeneral::initializeAvatar() { + QToolButton* avatarButton = m_ui->avatarButton; + avatarButtonMargins = avatarButton->size(); + + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + avatarButton->setIconSize(QSize(height, height)); +} + +void UI::ContactGeneral::onSetAvatar() { + avatarDiablog = new QFileDialog(this, tr("Chose your new avatar")); + avatarDiablog->setFileMode(QFileDialog::ExistingFile); + avatarDiablog->setNameFilter(tr("Images (*.png *.jpg *.jpeg)")); + + connect(avatarDiablog, &QFileDialog::accepted, this, &UI::ContactGeneral::avatarSelected); + connect(avatarDiablog, &QFileDialog::rejected, this, &UI::ContactGeneral::deleteAvatarDialog); + + avatarDiablog->show(); +} + +void UI::ContactGeneral::onClearAvatar() { + currentAvatarType = Shared::Avatar::empty; + currentAvatarPath = ""; + + updateAvatar(); +} + +void UI::ContactGeneral::updateAvatar() { + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + switch (currentAvatarType) { + case Shared::Avatar::empty: + m_ui->avatarButton->setIcon(Shared::icon("user", true)); + m_ui->avatarButton->setIconSize(QSize(height, height)); + m_ui->actionClearAvatar->setEnabled(false); + break; + case Shared::Avatar::autocreated: + case Shared::Avatar::valid: + QPixmap pixmap(currentAvatarPath); + qreal h = pixmap.height(); + qreal w = pixmap.width(); + qreal aspectRatio = w / h; + m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height)); + m_ui->avatarButton->setIcon(QIcon(currentAvatarPath)); + m_ui->actionClearAvatar->setEnabled(editable); + break; + } +} + +void UI::ContactGeneral::avatarSelected() { + QMimeDatabase db; + QString path = avatarDiablog->selectedFiles().front(); + QMimeType type = db.mimeTypeForFile(path); + deleteAvatarDialog(); + + if (supportedTypes.find(type.name()) == supportedTypes.end()) { + qDebug() << "Selected for avatar file is not supported, skipping"; + } else { + QImage src(path); + QImage dst; + if (src.width() > maxAvatarSize || src.height() > maxAvatarSize) { + dst = src.scaled(maxAvatarSize, maxAvatarSize, Qt::KeepAspectRatio); + } + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + currentJid + ".temp." + type.preferredSuffix(); + QFile oldTemp(path); + if (oldTemp.exists()) { + if (!oldTemp.remove()) { + qDebug() << "Error removing old temp avatar" << path; + return; + } + } + bool success = dst.save(path); + if (success) { + currentAvatarPath = path; + currentAvatarType = Shared::Avatar::valid; + + updateAvatar(); + } else { + qDebug() << "couldn't save temp avatar" << path << ", skipping"; + } + } +} + +void UI::ContactGeneral::setVCard(const QString& jid, const Shared::VCard& card, bool editable) { + currentJid = jid; + setEditable(editable); + m_ui->fullName->setText(card.getFullName()); + m_ui->firstName->setText(card.getFirstName()); + m_ui->middleName->setText(card.getMiddleName()); + m_ui->lastName->setText(card.getLastName()); + m_ui->nickName->setText(card.getNickName()); + m_ui->birthday->setDate(card.getBirthday()); + m_ui->organizationName->setText(card.getOrgName()); + m_ui->organizationDepartment->setText(card.getOrgUnit()); + m_ui->organizationTitle->setText(card.getOrgTitle()); + m_ui->organizationRole->setText(card.getOrgRole()); + + currentAvatarType = card.getAvatarType(); + currentAvatarPath = card.getAvatarPath(); + + updateAvatar(); +} diff --git a/ui/widgets/info/contactgeneral.h b/ui/widgets/info/contactgeneral.h index 5e116c6..6233c83 100644 --- a/ui/widgets/info/contactgeneral.h +++ b/ui/widgets/info/contactgeneral.h @@ -19,6 +19,18 @@ #include #include +#include +#include +#include +#include +#include +#include + +#include + +#include "shared/enums.h" +#include "shared/vcard.h" +#include "shared/icons.h" namespace UI { namespace Ui @@ -32,8 +44,32 @@ public: ContactGeneral(QWidget* parent = nullptr); ~ContactGeneral(); + void setVCard(const QString& jid, const Shared::VCard& card, bool editable = false); + QString title() const; + +private: + void setEditable(bool edit); + void initializeActions(); + void initializeAvatar(); + void updateAvatar(); + +private slots: + void deleteAvatarDialog(); + void avatarSelected(); + void onSetAvatar(); + void onClearAvatar(); + private: QScopedPointer m_ui; + QMenu* avatarMenu; + QSize avatarButtonMargins; + Shared::Avatar currentAvatarType; + QString currentAvatarPath; + QString currentJid; + bool editable; + QFileDialog* avatarDiablog; + + static const std::set supportedTypes; }; } diff --git a/ui/widgets/info/contactgeneral.ui b/ui/widgets/info/contactgeneral.ui index bb2b9e7..5ec123e 100644 --- a/ui/widgets/info/contactgeneral.ui +++ b/ui/widgets/info/contactgeneral.ui @@ -427,6 +427,24 @@ + + + + :/images/fallback/dark/big/edit-rename.svg:/images/fallback/dark/big/edit-rename.svg + + + Set avatar + + + + + + :/images/fallback/dark/big/clean.svg:/images/fallback/dark/big/clean.svg + + + Clear avatar + + fullName diff --git a/ui/widgets/info/description.cpp b/ui/widgets/info/description.cpp new file mode 100644 index 0000000..5132321 --- /dev/null +++ b/ui/widgets/info/description.cpp @@ -0,0 +1,27 @@ +// 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 "description.h" +#include "ui_description.h" + +UI::Description::Description(QWidget* parent): + QWidget(parent), + m_ui(new Ui::Description()) +{ + m_ui->setupUi(this); +} + +UI::Description::~Description() {} diff --git a/ui/widgets/info/description.h b/ui/widgets/info/description.h new file mode 100644 index 0000000..42b0900 --- /dev/null +++ b/ui/widgets/info/description.h @@ -0,0 +1,41 @@ +// 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_WIDGETS_DESCRIPTION_H +#define UI_WIDGETS_DESCRIPTION_H + +#include +#include + +namespace UI { +namespace Ui +{ +class Description; +} + +class Description : public QWidget { + Q_OBJECT +public: + Description(QWidget* parent = nullptr); + ~Description(); + +private: + QScopedPointer m_ui; +}; + +} + +#endif // UI_WIDGETS_DESCRIPTION_H diff --git a/ui/widgets/info/description.ui b/ui/widgets/info/description.ui new file mode 100644 index 0000000..e69a8f6 --- /dev/null +++ b/ui/widgets/info/description.ui @@ -0,0 +1,48 @@ + + + UI::Description + + + Description + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + font: 600 24pt ; + + + Description + + + + + + + QFrame::StyledPanel + + + Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index eacab0f..cbaffa8 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -17,14 +17,85 @@ #include "info.h" #include "ui_info.h" -UI::Info::Info(const Shared::Info& info, QWidget* parent): +UI::Info::Info(QWidget* parent): QWidget(parent), - m_ui(new Ui::Info()) + m_ui(new Ui::Info()), + contactGeneral(nullptr), + contactContacts(nullptr), + description(nullptr), + overlay(new QWidget()), + progress(new Progress(100)), + progressLabel(new QLabel()) { m_ui->setupUi(this); - + initializeOverlay(); } -UI::Info::~Info() -{} +UI::Info::~Info() { + if (contactGeneral != nullptr) + contactGeneral->deleteLater(); + + if (contactContacts != nullptr) + contactContacts->deleteLater(); + + if (description != nullptr) + description->deleteLater(); + + overlay->deleteLater(); +} + +void UI::Info::setData(const Shared::Info& info) { + switch (info.type) { + case Shared::EntryType::contact: + initializeContactGeneral(info); + initializeContactContacts(info); + initializeDescription(info.editable); + break; + default: + break; + } +} + +void UI::Info::initializeOverlay() { + QGridLayout* gr = static_cast(layout()); + gr->addWidget(overlay, 0, 0, 4, 1); + QVBoxLayout* nl = new QVBoxLayout(); + QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); + opacity->setOpacity(0.8); + overlay->setLayout(nl); + overlay->setBackgroundRole(QPalette::Base); + overlay->setAutoFillBackground(true); + overlay->setGraphicsEffect(opacity); + progressLabel->setAlignment(Qt::AlignCenter); + QFont pf = progressLabel->font(); + pf.setBold(true); + pf.setPointSize(26); + progressLabel->setFont(pf); + progressLabel->setWordWrap(true); + nl->addStretch(); + nl->addWidget(progress); + nl->addWidget(progressLabel); + nl->addStretch(); + overlay->hide(); +} + + +void UI::Info::showProgress(const QString& line) { + progressLabel->setText(line); + overlay->show(); + progress->start(); +} + +void UI::Info::hideProgress() { + overlay->hide(); + progress->stop(); +} + +void UI::Info::initializeContactGeneral(const Shared::Info& info) { + if (contactGeneral == nullptr) { + contactGeneral = new ContactGeneral; + m_ui->tabWidget->addTab(contactGeneral, contactGeneral->title()); + } + contactGeneral->setVCard(info.jid, info.vcard, info.editable); +} diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h index 6858074..126a092 100644 --- a/ui/widgets/info/info.h +++ b/ui/widgets/info/info.h @@ -19,10 +19,16 @@ #include #include +#include +#include #include +#include "ui/utils/progress.h" + #include "contactgeneral.h" +#include "contactcontacts.h" +#include "description.h" namespace UI { namespace Ui @@ -33,11 +39,28 @@ class Info; class Info : public QWidget { Q_OBJECT public: - Info(const Shared::Info& info, QWidget* parent = nullptr); + Info(QWidget* parent = nullptr); ~Info(); + void setData(const Shared::Info& info); + void showProgress(const QString& = ""); + void hideProgress(); + +private: + void initializeContactGeneral(const Shared::Info& info); + void initializeContactContacts(const Shared::Info& info); + void initializeDescription(bool editable); + void initializeOverlay(); + private: QScopedPointer m_ui; + ContactGeneral* contactGeneral; + ContactContacts* contactContacts; + Description* description; + QWidget* overlay; + Progress* progress; + QLabel* progressLabel; + }; } diff --git a/ui/widgets/info/info.ui b/ui/widgets/info/info.ui index f600d8b..ed2d42e 100644 --- a/ui/widgets/info/info.ui +++ b/ui/widgets/info/info.ui @@ -10,7 +10,23 @@ 300 - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + 6 @@ -76,49 +92,7 @@ false - - - Description - - - - 0 - - - 6 - - - 0 - - - 0 - - - 6 - - - - - font: 600 24pt ; - - - Description - - - - - - - QFrame::StyledPanel - - - Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - + @@ -131,6 +105,8 @@ + +