keep going on refactoring vcard

This commit is contained in:
Blue 2023-02-02 21:39:38 +03:00
parent 4af16b75bf
commit edf1ee60cd
Signed by: blue
GPG Key ID: 9B203B252A63EE38
13 changed files with 483 additions and 55 deletions

View File

@ -118,6 +118,16 @@ Q_ENUM_NS(AccountPassword)
static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet; static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet;
static const AccountPassword AccountPasswordLowest = AccountPassword::plain; 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 { enum class Support {
unknown, unknown,
supported, supported,

View File

@ -16,7 +16,8 @@
#include "info.h" #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), jid(p_jid),
editable(p_editable), editable(p_editable),
vcard(), vcard(),
@ -25,6 +26,7 @@ Shared::Info::Info(const QString& p_jid, bool p_editable):
{} {}
Shared::Info::Info(): Shared::Info::Info():
type(EntryType::contact),
jid(), jid(),
editable(false), editable(false),
vcard(), vcard(),
@ -33,6 +35,7 @@ Shared::Info::Info():
{} {}
Shared::Info::Info(const Shared::Info& other): Shared::Info::Info(const Shared::Info& other):
type(other.type),
jid(other.jid), jid(other.jid),
editable(other.editable), editable(other.editable),
vcard(other.vcard), vcard(other.vcard),

View File

@ -19,6 +19,7 @@
#include "vcard.h" #include "vcard.h"
#include "keyinfo.h" #include "keyinfo.h"
#include "enums.h"
#include <list> #include <list>
@ -33,10 +34,11 @@ namespace Shared {
class Info { class Info {
public: public:
Info(); Info();
Info(const QString& jid, bool editable = false); Info(const QString& jid, EntryType = EntryType::contact, bool editable = false);
Info(const Info& other); Info(const Info& other);
~Info(); ~Info();
EntryType type;
QString jid; QString jid;
bool editable; bool editable;
VCard vcard; VCard vcard;

View File

@ -2,18 +2,21 @@ set(SOURCE_FILES
info.cpp info.cpp
contactgeneral.cpp contactgeneral.cpp
contactcontacts.cpp contactcontacts.cpp
description.cpp
) )
set(UI_FILES set(UI_FILES
info.ui info.ui
contactgeneral.ui contactgeneral.ui
contactcontacts.ui contactcontacts.ui
description.ui
) )
set(HEADER_FILES set(HEADER_FILES
info.h info.h
contactgeneral.h contactgeneral.h
contactcontacts.h contactcontacts.h
description.h
) )
target_sources(squawk PRIVATE target_sources(squawk PRIVATE

View File

@ -17,12 +17,182 @@
#include "contactgeneral.h" #include "contactgeneral.h"
#include "ui_contactgeneral.h" #include "ui_contactgeneral.h"
#include <QDebug>
const std::set<QString> UI::ContactGeneral::supportedTypes = {"image/jpeg", "image/png"};
constexpr int maxAvatarSize = 160;
UI::ContactGeneral::ContactGeneral(QWidget* parent): UI::ContactGeneral::ContactGeneral(QWidget* parent):
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); 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();
}

View File

@ -19,6 +19,18 @@
#include <QWidget> #include <QWidget>
#include <QScopedPointer> #include <QScopedPointer>
#include <QMenu>
#include <QString>
#include <QFileDialog>
#include <QMimeDatabase>
#include <QStandardPaths>
#include <QImage>
#include <set>
#include "shared/enums.h"
#include "shared/vcard.h"
#include "shared/icons.h"
namespace UI { namespace UI {
namespace Ui namespace Ui
@ -32,8 +44,32 @@ public:
ContactGeneral(QWidget* parent = nullptr); ContactGeneral(QWidget* parent = nullptr);
~ContactGeneral(); ~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: private:
QScopedPointer<Ui::ContactGeneral> m_ui; QScopedPointer<Ui::ContactGeneral> m_ui;
QMenu* avatarMenu;
QSize avatarButtonMargins;
Shared::Avatar currentAvatarType;
QString currentAvatarPath;
QString currentJid;
bool editable;
QFileDialog* avatarDiablog;
static const std::set<QString> supportedTypes;
}; };
} }

View File

@ -427,6 +427,24 @@
</spacer> </spacer>
</item> </item>
</layout> </layout>
<action name="actionSetAvatar">
<property name="icon">
<iconset theme="photo" resource="../../../resources/resources.qrc">
<normaloff>:/images/fallback/dark/big/edit-rename.svg</normaloff>:/images/fallback/dark/big/edit-rename.svg</iconset>
</property>
<property name="text">
<string>Set avatar</string>
</property>
</action>
<action name="actionClearAvatar">
<property name="icon">
<iconset theme="edit-clear-all" resource="../../../resources/resources.qrc">
<normaloff>:/images/fallback/dark/big/clean.svg</normaloff>:/images/fallback/dark/big/clean.svg</iconset>
</property>
<property name="text">
<string>Clear avatar</string>
</property>
</action>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>fullName</tabstop> <tabstop>fullName</tabstop>

View File

@ -0,0 +1,27 @@
// 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 "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() {}

View File

@ -0,0 +1,41 @@
// 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_WIDGETS_DESCRIPTION_H
#define UI_WIDGETS_DESCRIPTION_H
#include <QWidget>
#include <QScopedPointer>
namespace UI {
namespace Ui
{
class Description;
}
class Description : public QWidget {
Q_OBJECT
public:
Description(QWidget* parent = nullptr);
~Description();
private:
QScopedPointer<Ui::Description> m_ui;
};
}
#endif // UI_WIDGETS_DESCRIPTION_H

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UI::Description</class>
<widget class="QWidget" name="Description">
<attribute name="title">
<string>Description</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="descriptionHeading">
<property name="styleSheet">
<string notr="true">font: 600 24pt ;</string>
</property>
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QTextEdit" name="description">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -17,14 +17,85 @@
#include "info.h" #include "info.h"
#include "ui_info.h" #include "ui_info.h"
UI::Info::Info(const Shared::Info& info, QWidget* parent): UI::Info::Info(QWidget* parent):
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); 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<QGridLayout*>(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);
}

View File

@ -19,10 +19,16 @@
#include <QWidget> #include <QWidget>
#include <QScopedPointer> #include <QScopedPointer>
#include <QGraphicsOpacityEffect>
#include <QLabel>
#include <shared/info.h> #include <shared/info.h>
#include "ui/utils/progress.h"
#include "contactgeneral.h" #include "contactgeneral.h"
#include "contactcontacts.h"
#include "description.h"
namespace UI { namespace UI {
namespace Ui namespace Ui
@ -33,11 +39,28 @@ class Info;
class Info : public QWidget { class Info : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
Info(const Shared::Info& info, QWidget* parent = nullptr); Info(QWidget* parent = nullptr);
~Info(); ~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: private:
QScopedPointer<Ui::Info> m_ui; QScopedPointer<Ui::Info> m_ui;
ContactGeneral* contactGeneral;
ContactContacts* contactContacts;
Description* description;
QWidget* overlay;
Progress* progress;
QLabel* progressLabel;
}; };
} }

View File

@ -10,7 +10,23 @@
<height>300</height> <height>300</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>6</number> <number>6</number>
@ -76,49 +92,7 @@
<property name="tabBarAutoHide"> <property name="tabBarAutoHide">
<bool>false</bool> <bool>false</bool>
</property> </property>
<widget class="QWidget" name="Description"> </widget>
<attribute name="title">
<string>Description</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="descriptionHeading">
<property name="styleSheet">
<string notr="true">font: 600 24pt ;</string>
</property>
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QTextEdit" name="description">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
@ -131,6 +105,8 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</item>
</layout>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>