diff --git a/qtox.pro b/qtox.pro index f2548a76e..42c2b0c4f 100644 --- a/qtox.pro +++ b/qtox.pro @@ -493,7 +493,8 @@ SOURCES += \ src/widget/genericchatitemwidget.cpp \ src/widget/friendlistlayout.cpp \ src/widget/genericchatitemlayout.cpp \ - src/widget/categorywidget.cpp + src/widget/categorywidget.cpp \ + src/widget/form/groupinviteform.cpp HEADERS += \ src/audio/audio.h \ @@ -536,4 +537,5 @@ HEADERS += \ src/widget/genericchatitemwidget.h \ src/widget/friendlistlayout.h \ src/widget/genericchatitemlayout.h \ - src/widget/categorywidget.h + src/widget/categorywidget.h \ + src/widget/form/groupinviteform.h diff --git a/src/core/core.cpp b/src/core/core.cpp index 946aab02f..0463944bc 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -1062,19 +1062,24 @@ void Core::groupInviteFriend(uint32_t friendId, int groupId) tox_invite_friend(tox, friendId, groupId); } -void Core::createGroup(uint8_t type) +int Core::createGroup(uint8_t type) { if (type == TOX_GROUPCHAT_TYPE_TEXT) { - emit emptyGroupCreated(tox_add_groupchat(tox)); + int group = tox_add_groupchat(tox); + emit emptyGroupCreated(group); + return group; } else if (type == TOX_GROUPCHAT_TYPE_AV) { - emit emptyGroupCreated(toxav_add_av_groupchat(tox, &Audio::playGroupAudioQueued, this)); + int group = toxav_add_av_groupchat(tox, &Audio::playGroupAudioQueued, this); + emit emptyGroupCreated(group); + return group; } else { qWarning() << "createGroup: Unknown type "<addTab(main, tr("Add a friend")); + tabWidget->addTab(main, QString()); QScrollArea* scrollArea = new QScrollArea(tabWidget); QWidget* requestWidget = new QWidget(tabWidget); scrollArea->setWidget(requestWidget); scrollArea->setWidgetResizable(true); requestsLayout = new QVBoxLayout(requestWidget); requestsLayout->addStretch(1); - tabWidget->addTab(scrollArea, tr("Friend requests")); + tabWidget->addTab(scrollArea, QString()); main->setLayout(&layout); layout.addWidget(&toxIdLabel); @@ -177,14 +177,15 @@ void AddFriendForm::setIdFromClipboard() toxId.setText(id); } } -#include + void AddFriendForm::onFriendRequestAccepted(QWidget* friendWidget) { int index = requestsLayout->indexOf(friendWidget); friendWidget->deleteLater(); requestsLayout->removeWidget(friendWidget); emit friendRequestAccepted(Settings::getInstance().getFriendRequest(requestsLayout->count() - index - 1).first); - qDebug() << "Accepted:" << requestsLayout->count() - index - 1; + Settings::getInstance().removeFriendRequest(requestsLayout->count() - index - 1); + Settings::getInstance().savePersonal(); } void AddFriendForm::onFriendRequestRejected(QWidget* friendWidget) @@ -194,7 +195,6 @@ void AddFriendForm::onFriendRequestRejected(QWidget* friendWidget) requestsLayout->removeWidget(friendWidget); Settings::getInstance().removeFriendRequest(requestsLayout->count() - index - 1); Settings::getInstance().savePersonal(); - qDebug() << "Rejected:" << requestsLayout->count() - index - 1; } void AddFriendForm::onCurrentChanged(int index) @@ -216,6 +216,9 @@ void AddFriendForm::retranslateUi() message.setPlaceholderText(tr("%1 here! Tox me maybe?", "Default message in friend requests if the field is left blank. Write something appropriate!") .arg(lastUsername)); + + tabWidget->setTabText(0, tr("Add a friend")); + tabWidget->setTabText(1, tr("Friend requests")); } void AddFriendForm::addFriendRequestWidget(const QString &friendAddress, const QString &message) @@ -226,7 +229,7 @@ void AddFriendForm::addFriendRequestWidget(const QString &friendAddress, const Q horLayout->setMargin(0); friendLayout->addLayout(horLayout); CroppingLabel* friendLabel = new CroppingLabel(friendWidget); - friendLabel->setText("" "12345678901234567890" ""); + friendLabel->setText("" + friendAddress + ""); horLayout->addWidget(friendLabel); QLabel* messageLabel = new QLabel(message); messageLabel->setWordWrap(true); diff --git a/src/widget/form/addfriendform.h b/src/widget/form/addfriendform.h index 741ec4554..2e917c696 100644 --- a/src/widget/form/addfriendform.h +++ b/src/widget/form/addfriendform.h @@ -38,7 +38,8 @@ public: enum Mode { AddFriend = 0, - FriendRequest = 1 + FriendRequest = 1, + GroupInvite = 2 }; AddFriendForm(); diff --git a/src/widget/form/groupinviteform.cpp b/src/widget/form/groupinviteform.cpp new file mode 100644 index 000000000..60673600e --- /dev/null +++ b/src/widget/form/groupinviteform.cpp @@ -0,0 +1,138 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre 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. + + qTox 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 qTox. If not, see . +*/ + + +#include "groupinviteform.h" +#include "ui_mainwindow.h" +#include "src/widget/tool/croppinglabel.h" +#include "src/nexus.h" +#include "src/core/core.h" +#include +#include +#include +#include +#include +#include +#include + +GroupInviteForm::GroupInviteForm() +{ + QVBoxLayout* layout = new QVBoxLayout(this); + createButton = new QPushButton(this); + connect(createButton, &QPushButton::released, [this]() + { + emit groupCreate(TOX_GROUPCHAT_TYPE_AV); + }); + + inviteBox = new QGroupBox(this); + inviteLayout = new QVBoxLayout(inviteBox); + inviteLayout->addStretch(1); + + layout->addWidget(createButton); + layout->addWidget(inviteBox); + + QFont bold; + bold.setBold(true); + + headLabel = new QLabel(this); + headLabel->setFont(bold); + headWidget = new QWidget(this); + QHBoxLayout* headLayout = new QHBoxLayout(headWidget); + headLayout->addWidget(headLabel); + + acceptMapper = new QSignalMapper(this); + rejectMapper = new QSignalMapper(this); + connect(acceptMapper, SIGNAL(mapped(QWidget*)), this, SLOT(onGroupInviteAccepted(QWidget*))); + connect(rejectMapper, SIGNAL(mapped(QWidget*)), this, SLOT(onGroupInviteRejected(QWidget*))); + + retranslateUi(); +} + +void GroupInviteForm::show(Ui::MainWindow &ui) +{ + ui.mainContent->layout()->addWidget(this); + ui.mainHead->layout()->addWidget(headWidget); + QWidget::show(); + headWidget->show(); +} + +void GroupInviteForm::addGroupInvite(int32_t friendId, uint8_t type, QByteArray invite) +{ + QWidget* groupWidget = new QWidget(this); + QHBoxLayout* groupLayout = new QHBoxLayout(groupWidget); + + CroppingLabel* groupLabel = new CroppingLabel(this); + groupLabel->setText(tr("Invited by %1 on %2.").arg(Nexus::getCore()->getFriendUsername(friendId), QDateTime::currentDateTime().toString())); + groupLayout->addWidget(groupLabel); + + QPushButton* acceptButton = new QPushButton(tr("Join")); + connect(acceptButton, SIGNAL(pressed()), acceptMapper,SLOT(map())); + acceptMapper->setMapping(acceptButton, groupWidget); + groupLayout->addWidget(acceptButton); + + QPushButton* rejectButton = new QPushButton(tr("Decline")); + connect(rejectButton, SIGNAL(pressed()), rejectMapper,SLOT(map())); + rejectMapper->setMapping(rejectButton, groupWidget); + groupLayout->addWidget(rejectButton); + + inviteLayout->insertWidget(0, groupWidget); + + GroupInvite group; + group.friendId = friendId; + group.type = type; + group.invite = invite; + groupInvites.push_front(group); + + if (isVisible()) + emit groupInvitesSeen(); +} + +void GroupInviteForm::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + emit groupInvitesSeen(); +} + +void GroupInviteForm::onGroupInviteAccepted(QWidget* groupWidget) +{ + int index = inviteLayout->indexOf(groupWidget); + GroupInvite invite = groupInvites.at(index); + groupInvites.removeAt(index); + + groupWidget->deleteLater(); + inviteLayout->removeWidget(groupWidget); + + emit groupInviteAccepted(invite.friendId, invite.type, invite.invite); +} + +void GroupInviteForm::onGroupInviteRejected(QWidget* groupWidget) +{ + int index = inviteLayout->indexOf(groupWidget); + groupInvites.removeAt(index); + + groupWidget->deleteLater(); + inviteLayout->removeWidget(groupWidget); +} + +void GroupInviteForm::retranslateUi() +{ + headLabel->setText(tr("Groups")); + createButton->setText(tr("Create new group")); + inviteBox->setTitle(tr("Group invites")); +} diff --git a/src/widget/form/groupinviteform.h b/src/widget/form/groupinviteform.h new file mode 100644 index 000000000..5de921a26 --- /dev/null +++ b/src/widget/form/groupinviteform.h @@ -0,0 +1,75 @@ +/* + Copyright © 2015 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre 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. + + qTox 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 qTox. If not, see . +*/ + +#ifndef GROUPINVITEFORM_H +#define GROUPINVITEFORM_H + +#include + +class QLabel; +class QVBoxLayout; +class QPushButton; +class QGroupBox; +class QSignalMapper; + +namespace Ui {class MainWindow;} + +class GroupInviteForm : public QWidget +{ + Q_OBJECT +public: + GroupInviteForm(); + + void show(Ui::MainWindow &ui); + void addGroupInvite(int32_t friendId, uint8_t type, QByteArray invite); + +signals: + void groupCreate(uint8_t type); + void groupInviteAccepted(int32_t friendId, uint8_t type, QByteArray invite); + void groupInvitesSeen(); + +protected: + void showEvent(QShowEvent* event) final override; + +private slots: + void onGroupInviteAccepted(QWidget* groupWidget); + void onGroupInviteRejected(QWidget* groupWidget); + +private: + void retranslateUi(); + +private: + struct GroupInvite + { + int32_t friendId; + uint8_t type; + QByteArray invite; + }; + + QWidget* headWidget; + QLabel* headLabel; + QPushButton* createButton; + QGroupBox* inviteBox; + QVBoxLayout* inviteLayout; + QSignalMapper* acceptMapper; + QSignalMapper* rejectMapper; + QList groupInvites; +}; + +#endif // GROUPINVITEFORM_H diff --git a/src/widget/friendwidget.cpp b/src/widget/friendwidget.cpp index cf4a7ce8a..b4d5828ec 100644 --- a/src/widget/friendwidget.cpp +++ b/src/widget/friendwidget.cpp @@ -68,17 +68,16 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event) QString dir = Settings::getInstance().getAutoAcceptDir(id); QMenu menu; QMenu* inviteMenu = menu.addMenu(tr("Invite to group","Menu to invite a friend to a groupchat")); + QAction* newGroupAction = inviteMenu->addAction(tr("To new group")); + inviteMenu->addSeparator(); QMap groupActions; for (Group* group : GroupList::getAllGroups()) { - QAction* groupAction = inviteMenu->addAction(group->getGroupWidget()->getName()); + QAction* groupAction = inviteMenu->addAction(tr("Invite to group '%1'").arg(group->getGroupWidget()->getName())); groupActions[groupAction] = group; } - if (groupActions.isEmpty()) - inviteMenu->setEnabled(false); - int circleId = Settings::getInstance().getFriendCircleID(FriendList::findFriend(friendId)->getToxId()); CircleWidget *circleWidget = CircleWidget::getFromID(circleId); @@ -174,6 +173,11 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event) Settings::getInstance().setAutoAcceptDir(id, dir); } } + else if (selectedItem == newGroupAction) + { + int groupId = Core::getInstance()->createGroup(); + Core::getInstance()->groupInviteFriend(friendId, groupId); + } else if (selectedItem == newCircleAction) { if (circleWidget != nullptr) diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 50f69d0bf..6b719f79d 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -44,6 +44,7 @@ #include "src/persistence/offlinemsgengine.h" #include "src/widget/translator.h" #include "src/widget/form/addfriendform.h" +#include "src/widget/form/groupinviteform.h" #include "src/widget/form/filesform.h" #include "src/widget/form/profileform.h" #include "src/widget/form/settingswidget.h" @@ -224,6 +225,7 @@ void Widget::init() filesForm = new FilesForm(); addFriendForm = new AddFriendForm; + groupInviteForm = new GroupInviteForm; profileForm = new ProfileForm(); settingsWidget = new SettingsWidget(); @@ -241,6 +243,7 @@ void Widget::init() connect(ui->statusLabel, &CroppingLabel::editFinished, this, &Widget::onStatusMessageChanged); connect(ui->mainSplitter, &QSplitter::splitterMoved, this, &Widget::onSplitterMoved); connect(addFriendForm, &AddFriendForm::friendRequested, this, &Widget::friendRequested); + connect(groupInviteForm, &GroupInviteForm::groupCreate, Core::getInstance(), &Core::createGroup); connect(timer, &QTimer::timeout, this, &Widget::onUserAwayCheck); connect(timer, &QTimer::timeout, this, &Widget::onEventIconTick); connect(timer, &QTimer::timeout, this, &Widget::onTryCreateTrayIcon); @@ -344,17 +347,20 @@ void Widget::init() AutoUpdater::checkUpdatesAsyncInteractive(); #endif + friendRequestsButton = nullptr; + groupInvitesButton = nullptr; + unreadGroupInvites = 0; + + connect(addFriendForm, &AddFriendForm::friendRequestsSeen, this, &Widget::friendRequestsUpdate); + connect(addFriendForm, &AddFriendForm::friendRequestAccepted, this, &Widget::friendRequestAccepted); + connect(groupInviteForm, &GroupInviteForm::groupInvitesSeen, this, &Widget::groupInvitesClear); + connect(groupInviteForm, &GroupInviteForm::groupInviteAccepted, this, &Widget::onGroupInviteAccepted); + retranslateUi(); Translator::registerHandler(std::bind(&Widget::retranslateUi, this), this); if (!Settings::getInstance().getShowSystemTray()) show(); - - friendRequestsButton = nullptr; - friendRequestsUpdate(); - - connect(addFriendForm, &AddFriendForm::friendRequestsSeen, this, &Widget::friendRequestsUpdate); - connect(addFriendForm, &AddFriendForm::friendRequestAccepted, this, &Widget::friendRequestAccepted); } bool Widget::eventFilter(QObject *obj, QEvent *event) @@ -414,6 +420,7 @@ Widget::~Widget() delete profileForm; delete settingsWidget; delete addFriendForm; + delete groupInviteForm; delete filesForm; delete timer; delete offlineMsgTimer; @@ -549,7 +556,11 @@ void Widget::onAddClicked() void Widget::onGroupClicked() { - Nexus::getCore()->createGroup(); + hideMainForms(); + groupInviteForm->show(*ui); + setWindowTitle(tr("Group invites")); + setActiveToolMenuButton(Widget::GroupButton); + activeChatroomWidget = nullptr; } void Widget::onTransferClicked() @@ -1062,15 +1073,9 @@ void Widget::onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray in { if (type == TOX_GROUPCHAT_TYPE_TEXT || type == TOX_GROUPCHAT_TYPE_AV) { - if (GUI::askQuestion(tr("Group invite", "popup title"), tr("%1 has invited you to a groupchat. Would you like to join?", "popup text").arg(Nexus::getCore()->getFriendUsername(friendId)), true, false)) - { - int groupId = Nexus::getCore()->joinGroupchat(friendId, type, (uint8_t*)invite.data(), invite.length()); - if (groupId < 0) - { - qWarning() << "onGroupInviteReceived: Unable to accept group invite"; - return; - } - } + ++unreadGroupInvites; + groupInvitesUpdate(); + groupInviteForm->addGroupInvite(friendId, type, invite); } else { @@ -1079,6 +1084,16 @@ void Widget::onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray in } } +void Widget::onGroupInviteAccepted(int32_t friendId, uint8_t type, QByteArray invite) +{ + int groupId = Nexus::getCore()->joinGroupchat(friendId, type, (uint8_t*)invite.data(), invite.length()); + if (groupId < 0) + { + qWarning() << "onGroupInviteAccepted: Unable to accept group invite"; + return; + } +} + void Widget::onGroupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction) { Group* g = GroupList::findGroup(groupnumber); @@ -1669,11 +1684,14 @@ bool Widget::groupsVisible() const void Widget::friendListContextMenu(const QPoint &pos) { QMenu menu(this); + QAction *createGroupAction = menu.addAction(tr("Create new group...")); QAction *addCircleAction = menu.addAction(tr("Add new circle...")); QAction *chosenAction = menu.exec(ui->friendList->mapToGlobal(pos)); if (chosenAction == addCircleAction) contactListWidget->addCircleWidget(); + else if (chosenAction == createGroupAction) + Nexus::getCore()->createGroup(); } void Widget::friendRequestRecieved(const QString& friendAddress, const QString& message) @@ -1691,21 +1709,46 @@ void Widget::friendRequestsUpdate() delete friendRequestsButton; friendRequestsButton = nullptr; } - else + else if (!friendRequestsButton) { - if (!friendRequestsButton) - { - friendRequestsButton = new QPushButton(tr("%n New Friend Request(s)", "", unreadFriendRequests)); - friendRequestsButton->setObjectName("green"); - } - + friendRequestsButton = new QPushButton(this); + friendRequestsButton->setObjectName("green"); ui->statusLayout->insertWidget(2, friendRequestsButton); + connect(friendRequestsButton, &QPushButton::released, [this]() { - onAddClicked(); - addFriendForm->setMode(AddFriendForm::FriendRequest); + onGroupClicked(); }); } + + if (friendRequestsButton) + friendRequestsButton->setText(tr("%n New Friend Request(s)", "", unreadFriendRequests)); +} + +void Widget::groupInvitesUpdate() +{ + if (unreadGroupInvites == 0) + { + delete groupInvitesButton; + groupInvitesButton = nullptr; + } + else if (!groupInvitesButton) + { + groupInvitesButton = new QPushButton(this); + groupInvitesButton->setObjectName("green"); + ui->statusLayout->insertWidget(2, groupInvitesButton); + + connect(groupInvitesButton, &QPushButton::released, this, &Widget::onGroupClicked); + } + + if (groupInvitesButton) + groupInvitesButton->setText(tr("%n New Group Invite(s)", "", unreadGroupInvites)); +} + +void Widget::groupInvitesClear() +{ + unreadGroupInvites = 0; + groupInvitesUpdate(); } void Widget::setActiveToolMenuButton(ActiveToolMenuButton newActiveButton) @@ -1742,6 +1785,9 @@ void Widget::retranslateUi() statusAway->setText(tr("Away", "Button to set your status to 'Away'")); statusBusy->setText(tr("Busy", "Button to set your status to 'Busy'")); setWindowTitle(tr("Settings")); + + friendRequestsUpdate(); + groupInvitesUpdate(); } #ifdef Q_OS_MAC diff --git a/src/widget/widget.h b/src/widget/widget.h index 849d88d7f..ea93484b0 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -49,6 +49,7 @@ class FilesForm; class ProfileForm; class SettingsWidget; class AddFriendForm; +class GroupInviteForm; class CircleWidget; class QActionGroup; class QPushButton; @@ -112,6 +113,7 @@ public slots: void onReceiptRecieved(int friendId, int receipt); void onEmptyGroupCreated(int groupId); void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite); + void onGroupInviteAccepted(int32_t friendId, uint8_t type, QByteArray invite); void onGroupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction); void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change); void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title); @@ -162,6 +164,8 @@ private slots: void friendListContextMenu(const QPoint &pos); void friendRequestRecieved(const QString& friendAddress, const QString& message); void friendRequestsUpdate(); + void groupInvitesUpdate(); + void groupInvitesClear(); #ifdef Q_OS_MAC void bringAllToFront(); @@ -229,6 +233,7 @@ private: QSplitter *centralLayout; QPoint dragPosition; AddFriendForm *addFriendForm; + GroupInviteForm* groupInviteForm; ProfileForm *profileForm; SettingsWidget *settingsWidget; FilesForm *filesForm; @@ -245,6 +250,8 @@ private: bool eventIcon; bool wasMaximized = false; QPushButton* friendRequestsButton; + QPushButton* groupInvitesButton; + unsigned int unreadGroupInvites; #ifdef Q_OS_MAC QAction* fullscreenAction;