[Move refactor] Reparent orphan classes (#6236)

* Move orphaned classes to their correct parent folders.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2025-10-09 14:15:19 +02:00 committed by GitHub
parent 1ef07309d6
commit d9c65d4ae0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
143 changed files with 171 additions and 169 deletions

View file

@ -0,0 +1,536 @@
#include "user_context_menu.h"
#include "../../tabs/tab_account.h"
#include "../../tabs/tab_game.h"
#include "../../tabs/tab_supervisor.h"
#include "../chat_view/chat_view.h"
#include "../game_selector.h"
#include "user_info_box.h"
#include "user_list_manager.h"
#include "user_list_proxy.h"
#include "user_list_widget.h"
#include <QAction>
#include <QMenu>
#include <QMessageBox>
#include <QSignalMapper>
#include <QtGui>
#include <QtWidgets>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_kick_from_game.pb.h>
#include <libcockatrice/protocol/pb/commands.pb.h>
#include <libcockatrice/protocol/pb/moderator_commands.pb.h>
#include <libcockatrice/protocol/pb/response_ban_history.pb.h>
#include <libcockatrice/protocol/pb/response_get_admin_notes.pb.h>
#include <libcockatrice/protocol/pb/response_get_games_of_user.pb.h>
#include <libcockatrice/protocol/pb/response_get_user_info.pb.h>
#include <libcockatrice/protocol/pb/response_warn_history.pb.h>
#include <libcockatrice/protocol/pb/response_warn_list.pb.h>
#include <libcockatrice/protocol/pb/session_commands.pb.h>
#include <libcockatrice/protocol/pending_command.h>
UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent, AbstractGame *_game)
: QObject(parent), client(_tabSupervisor->getClient()), tabSupervisor(_tabSupervisor),
userListProxy(_tabSupervisor->getUserListManager()), game(_game)
{
aUserName = new QAction(QString(), this);
aUserName->setEnabled(false);
aDetails = new QAction(QString(), this);
aChat = new QAction(QString(), this);
aShowGames = new QAction(QString(), this);
aAddToBuddyList = new QAction(QString(), this);
aRemoveFromBuddyList = new QAction(QString(), this);
aAddToIgnoreList = new QAction(QString(), this);
aRemoveFromIgnoreList = new QAction(QString(), this);
aKick = new QAction(QString(), this);
aWarnUser = new QAction(QString(), this);
aWarnHistory = new QAction(QString(), this);
aBan = new QAction(QString(), this);
aBanHistory = new QAction(QString(), this);
aPromoteToMod = new QAction(QString(), this);
aDemoteFromMod = new QAction(QString(), this);
aPromoteToJudge = new QAction(QString(), this);
aDemoteFromJudge = new QAction(QString(), this);
aGetAdminNotes = new QAction(QString(), this);
retranslateUi();
}
void UserContextMenu::retranslateUi()
{
aDetails->setText(tr("User &details"));
aChat->setText(tr("Private &chat"));
aShowGames->setText(tr("Show this user's &games"));
aAddToBuddyList->setText(tr("Add to &buddy list"));
aRemoveFromBuddyList->setText(tr("Remove from &buddy list"));
aAddToIgnoreList->setText(tr("Add to &ignore list"));
aRemoveFromIgnoreList->setText(tr("Remove from &ignore list"));
aKick->setText(tr("Kick from &game"));
aWarnUser->setText(tr("Warn user"));
aWarnHistory->setText(tr("View user's war&n history"));
aBan->setText(tr("Ban from &server"));
aBanHistory->setText(tr("View user's &ban history"));
aPromoteToMod->setText(tr("&Promote user to moderator"));
aDemoteFromMod->setText(tr("Dem&ote user from moderator"));
aPromoteToJudge->setText(tr("Promote user to &judge"));
aDemoteFromJudge->setText(tr("Demote user from judge"));
aGetAdminNotes->setText(tr("View admin notes"));
}
void UserContextMenu::gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer)
{
const Command_GetGamesOfUser &cmd = commandContainer.session_command(0).GetExtension(Command_GetGamesOfUser::ext);
if (resp.response_code() == Response::RespNameNotFound) {
QMessageBox::critical(static_cast<QWidget *>(parent()), tr("Error"), tr("This user does not exist."));
return;
} else if (resp.response_code() == Response::RespInIgnoreList) {
QMessageBox::critical(
static_cast<QWidget *>(parent()), tr("Error"),
tr("You are being ignored by %1 and can't see their games.").arg(QString::fromStdString(cmd.user_name())));
return;
} else if (resp.response_code() != Response::RespOk) {
QMessageBox::critical(static_cast<QWidget *>(parent()), tr("Error"),
tr("Could not get %1's games.").arg(QString::fromStdString(cmd.user_name())));
return;
}
const Response_GetGamesOfUser &response = resp.GetExtension(Response_GetGamesOfUser::ext);
QMap<int, GameTypeMap> gameTypeMap;
QMap<int, QString> roomMap;
const int roomListSize = response.room_list_size();
for (int i = 0; i < roomListSize; ++i) {
const ServerInfo_Room &roomInfo = response.room_list(i);
roomMap.insert(roomInfo.room_id(), QString::fromStdString(roomInfo.name()));
GameTypeMap tempMap;
const int gameTypeListSize = roomInfo.gametype_list_size();
for (int j = 0; j < gameTypeListSize; ++j) {
const ServerInfo_GameType &gameTypeInfo = roomInfo.gametype_list(j);
tempMap.insert(gameTypeInfo.game_type_id(), QString::fromStdString(gameTypeInfo.description()));
}
gameTypeMap.insert(roomInfo.room_id(), tempMap);
}
GameSelector *selector = new GameSelector(client, tabSupervisor, nullptr, roomMap, gameTypeMap, false, false);
selector->setParent(static_cast<QWidget *>(parent()), Qt::Window);
const int gameListSize = response.game_list_size();
for (int i = 0; i < gameListSize; ++i) {
selector->processGameInfo(response.game_list(i));
}
selector->setWindowTitle(tr("%1's games").arg(QString::fromStdString(cmd.user_name())));
selector->setMinimumWidth(800);
selector->setAttribute(Qt::WA_DeleteOnClose);
selector->show();
}
void UserContextMenu::banUser_processUserInfoResponse(const Response &r)
{
const Response_GetUserInfo &response = r.GetExtension(Response_GetUserInfo::ext);
// The dialog needs to be non-modal in order to not block the event queue of the client.
BanDialog *dlg = new BanDialog(response.user_info(), static_cast<QWidget *>(parent()));
connect(dlg, &QDialog::accepted, this, &UserContextMenu::banUser_dialogFinished);
dlg->show();
}
void UserContextMenu::warnUser_processGetWarningsListResponse(const Response &r)
{
const Response_WarnList &response = r.GetExtension(Response_WarnList::ext);
QString user = QString::fromStdString(response.user_name()).simplified();
QString clientid = QString::fromStdString(response.user_clientid()).simplified();
// The dialog needs to be non-modal in order to not block the event queue of the client.
WarningDialog *dlg = new WarningDialog(user, clientid, static_cast<QWidget *>(parent()));
connect(dlg, &QDialog::accepted, this, &UserContextMenu::warnUser_dialogFinished);
if (response.warning_size() > 0) {
for (int i = 0; i < response.warning_size(); ++i) {
dlg->addWarningOption(QString::fromStdString(response.warning(i)).simplified());
}
}
dlg->show();
}
void UserContextMenu::warnUser_processUserInfoResponse(const Response &resp)
{
const Response_GetUserInfo &response = resp.GetExtension(Response_GetUserInfo::ext);
ServerInfo_User userInfo = response.user_info();
Command_GetWarnList cmd;
cmd.set_user_name(userInfo.name());
cmd.set_user_clientid(userInfo.clientid());
PendingCommand *pend = client->prepareModeratorCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::warnUser_processGetWarningsListResponse);
client->sendCommand(pend);
}
void UserContextMenu::banUserHistory_processResponse(const Response &resp)
{
const Response_BanHistory &response = resp.GetExtension(Response_BanHistory::ext);
if (resp.response_code() == Response::RespOk) {
if (response.ban_list_size() > 0) {
QTableWidget *table = new QTableWidget();
table->setWindowTitle(tr("Ban History"));
table->setRowCount(response.ban_list_size());
table->setColumnCount(5);
table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
table->setHorizontalHeaderLabels(
QString(tr("Ban Time;Moderator;Ban Length;Ban Reason;Visible Reason")).split(";"));
ServerInfo_Ban ban;
for (int i = 0; i < response.ban_list_size(); ++i) {
ban = response.ban_list(i);
table->setItem(i, 0, new QTableWidgetItem(QString::fromStdString(ban.ban_time())));
table->setItem(i, 1, new QTableWidgetItem(QString::fromStdString(ban.admin_name())));
table->setItem(i, 2, new QTableWidgetItem(QString::fromStdString(ban.ban_length())));
table->setItem(i, 3, new QTableWidgetItem(QString::fromStdString(ban.ban_reason())));
table->setItem(i, 4, new QTableWidgetItem(QString::fromStdString(ban.visible_reason())));
}
table->resizeColumnsToContents();
table->setMinimumSize(table->horizontalHeader()->length() + (table->columnCount() * 5),
table->verticalHeader()->length() + (table->rowCount() * 3));
table->show();
} else
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Ban History"),
tr("User has never been banned."));
} else
QMessageBox::critical(static_cast<QWidget *>(parent()), tr("Ban History"),
tr("Failed to collect ban information."));
}
void UserContextMenu::warnUserHistory_processResponse(const Response &resp)
{
const Response_WarnHistory &response = resp.GetExtension(Response_WarnHistory::ext);
if (resp.response_code() == Response::RespOk) {
if (response.warn_list_size() > 0) {
QTableWidget *table = new QTableWidget();
table->setWindowTitle(tr("Warning History"));
table->setRowCount(response.warn_list_size());
table->setColumnCount(4);
table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
table->setHorizontalHeaderLabels(QString(tr("Warning Time;Moderator;User Name;Reason")).split(";"));
ServerInfo_Warning warn;
for (int i = 0; i < response.warn_list_size(); ++i) {
warn = response.warn_list(i);
table->setItem(i, 0, new QTableWidgetItem(QString::fromStdString(warn.time_of())));
table->setItem(i, 1, new QTableWidgetItem(QString::fromStdString(warn.admin_name())));
table->setItem(i, 2, new QTableWidgetItem(QString::fromStdString(warn.user_name())));
table->setItem(i, 3, new QTableWidgetItem(QString::fromStdString(warn.reason())));
}
table->resizeColumnsToContents();
table->setMinimumSize(table->horizontalHeader()->length() + (table->columnCount() * 5),
table->verticalHeader()->length() + (table->rowCount() * 3));
table->show();
} else
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Warning History"),
tr("User has never been warned."));
} else
QMessageBox::critical(static_cast<QWidget *>(parent()), tr("Warning History"),
tr("Failed to collect warning information."));
}
void UserContextMenu::getAdminNotes_processResponse(const Response &resp)
{
const Response_GetAdminNotes &response = resp.GetExtension(Response_GetAdminNotes::ext);
if (resp.response_code() != Response::RespOk) {
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Failed"), tr("Failed to get admin notes."));
return;
}
auto *dlg = new AdminNotesDialog(QString::fromStdString(response.user_name()),
QString::fromStdString(response.notes()), static_cast<QWidget *>(parent()));
connect(dlg, &AdminNotesDialog::accepted, this, &UserContextMenu::updateAdminNotes_dialogFinished);
dlg->show();
}
void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const CommandContainer &commandContainer)
{
const Command_AdjustMod &cmd = commandContainer.admin_command(0).GetExtension(Command_AdjustMod::ext);
if (resp.response_code() == Response::RespOk) {
if (cmd.should_be_mod() || cmd.should_be_judge()) {
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Success"),
tr("Successfully promoted user."));
} else {
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Success"), tr("Successfully demoted user."));
}
} else {
if (cmd.should_be_mod() || cmd.should_be_judge()) {
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Failed"), tr("Failed to promote user."));
} else {
QMessageBox::information(static_cast<QWidget *>(parent()), tr("Failed"), tr("Failed to demote user."));
}
}
}
void UserContextMenu::banUser_dialogFinished()
{
BanDialog *dlg = static_cast<BanDialog *>(sender());
Command_BanFromServer cmd;
cmd.set_user_name(dlg->getBanName().toStdString());
cmd.set_address(dlg->getBanIP().toStdString());
cmd.set_minutes(dlg->getMinutes());
cmd.set_reason(dlg->getReason().toStdString());
cmd.set_visible_reason(dlg->getVisibleReason().toStdString());
cmd.set_clientid(dlg->getBanId().toStdString());
int removeAmount = dlg->getDeleteMessages();
if (removeAmount != 0) {
cmd.set_remove_messages(removeAmount);
}
client->sendCommand(client->prepareModeratorCommand(cmd));
}
void UserContextMenu::warnUser_dialogFinished()
{
WarningDialog *dlg = static_cast<WarningDialog *>(sender());
if (dlg->getName().isEmpty() || userListProxy->getOwnUsername().simplified().isEmpty())
return;
Command_WarnUser cmd;
cmd.set_user_name(dlg->getName().toStdString());
cmd.set_reason(dlg->getReason().toStdString());
cmd.set_clientid(dlg->getWarnID().toStdString());
int removeAmount = dlg->getDeleteMessages();
if (removeAmount != 0) {
cmd.set_remove_messages(removeAmount);
}
client->sendCommand(client->prepareModeratorCommand(cmd));
}
void UserContextMenu::updateAdminNotes_dialogFinished()
{
auto *dlg = static_cast<AdminNotesDialog *>(sender());
Command_UpdateAdminNotes cmd;
cmd.set_user_name(dlg->getName().toStdString());
cmd.set_notes(dlg->getNotes().toStdString());
client->sendCommand(client->prepareModeratorCommand(cmd));
}
void UserContextMenu::showContextMenu(const QPoint &pos,
const QString &userName,
UserLevelFlags userLevel,
bool online,
int playerId)
{
showContextMenu(pos, userName, userLevel, online, playerId, QString(), nullptr);
}
void UserContextMenu::showContextMenu(const QPoint &pos,
const QString &userName,
UserLevelFlags userLevel,
ChatView *chatView)
{
showContextMenu(pos, userName, userLevel, true, -1, QString(), chatView);
}
void UserContextMenu::showContextMenu(const QPoint &pos,
const QString &userName,
UserLevelFlags userLevel,
bool online,
int playerId,
const QString &deckHash,
ChatView *chatView)
{
QAction *aCopyToClipBoard = nullptr, *aRemoveMessages = nullptr;
aUserName->setText(userName);
auto *menu = new QMenu(static_cast<QWidget *>(parent()));
menu->addAction(aUserName);
menu->addSeparator();
if (!deckHash.isEmpty()) {
aCopyToClipBoard = new QAction(tr("Copy hash to clipboard"), this);
menu->addAction(aCopyToClipBoard);
}
menu->addAction(aDetails);
menu->addAction(aShowGames);
menu->addAction(aChat);
if (userLevel.testFlag(ServerInfo_User::IsRegistered) && userListProxy->isOwnUserRegistered()) {
menu->addSeparator();
if (userListProxy->isUserBuddy(userName)) {
menu->addAction(aRemoveFromBuddyList);
} else {
menu->addAction(aAddToBuddyList);
}
if (userListProxy->isUserIgnored(userName)) {
menu->addAction(aRemoveFromIgnoreList);
} else {
menu->addAction(aAddToIgnoreList);
}
}
if (chatView != nullptr) {
aRemoveMessages = new QAction(tr("Remove this user's messages"), this);
menu->addAction(aRemoveMessages);
}
if (game && (game->isHost() || !tabSupervisor->getAdminLocked())) {
menu->addSeparator();
menu->addAction(aKick);
}
if (!tabSupervisor->getAdminLocked()) {
menu->addSeparator();
menu->addAction(aWarnUser);
menu->addAction(aWarnHistory);
menu->addSeparator();
menu->addAction(aBan);
menu->addAction(aBanHistory);
menu->addSeparator();
menu->addAction(aGetAdminNotes);
menu->addSeparator();
if (userLevel.testFlag(ServerInfo_User::IsModerator) &&
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
menu->addAction(aDemoteFromMod);
} else if (userLevel.testFlag(ServerInfo_User::IsRegistered) &&
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
menu->addAction(aPromoteToMod);
}
if (userLevel.testFlag(ServerInfo_User::IsJudge) &&
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
menu->addAction(aDemoteFromJudge);
} else if (userLevel.testFlag(ServerInfo_User::IsRegistered) &&
(tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) {
menu->addAction(aPromoteToJudge);
}
}
bool anotherUser = userName != userListProxy->getOwnUsername();
aDetails->setEnabled(true);
aChat->setEnabled(anotherUser && online);
aShowGames->setEnabled(online);
aAddToBuddyList->setEnabled(anotherUser);
aRemoveFromBuddyList->setEnabled(anotherUser);
aAddToIgnoreList->setEnabled(anotherUser);
aRemoveFromIgnoreList->setEnabled(anotherUser);
aKick->setEnabled(anotherUser);
aWarnUser->setEnabled(anotherUser);
aWarnHistory->setEnabled(anotherUser);
aBan->setEnabled(anotherUser);
aBanHistory->setEnabled(anotherUser);
aGetAdminNotes->setEnabled(anotherUser);
aPromoteToMod->setEnabled(anotherUser);
aDemoteFromMod->setEnabled(anotherUser);
QAction *actionClicked = menu->exec(pos);
if (actionClicked == nullptr) {
} else if (actionClicked == aDetails) {
UserInfoBox *infoWidget =
new UserInfoBox(client, false, static_cast<QWidget *>(parent()),
Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
infoWidget->setAttribute(Qt::WA_DeleteOnClose);
infoWidget->updateInfo(userName);
} else if (actionClicked == aChat) {
emit openMessageDialog(userName, true);
} else if (actionClicked == aShowGames) {
Command_GetGamesOfUser cmd;
cmd.set_user_name(userName.toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::gamesOfUserReceived);
client->sendCommand(pend);
} else if (actionClicked == aAddToBuddyList) {
Command_AddToList cmd;
cmd.set_list("buddy");
cmd.set_user_name(userName.toStdString());
client->sendCommand(client->prepareSessionCommand(cmd));
} else if (actionClicked == aRemoveFromBuddyList) {
Command_RemoveFromList cmd;
cmd.set_list("buddy");
cmd.set_user_name(userName.toStdString());
client->sendCommand(client->prepareSessionCommand(cmd));
} else if (actionClicked == aAddToIgnoreList) {
Command_AddToList cmd;
cmd.set_list("ignore");
cmd.set_user_name(userName.toStdString());
client->sendCommand(client->prepareSessionCommand(cmd));
} else if (actionClicked == aRemoveFromIgnoreList) {
Command_RemoveFromList cmd;
cmd.set_list("ignore");
cmd.set_user_name(userName.toStdString());
client->sendCommand(client->prepareSessionCommand(cmd));
} else if (actionClicked == aKick) {
Command_KickFromGame cmd;
cmd.set_player_id(playerId);
game->getGameEventHandler()->sendGameCommand(cmd);
} else if (actionClicked == aBan) {
Command_GetUserInfo cmd;
cmd.set_user_name(userName.toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::banUser_processUserInfoResponse);
client->sendCommand(pend);
} else if (actionClicked == aPromoteToMod || actionClicked == aDemoteFromMod) {
Command_AdjustMod cmd;
cmd.set_user_name(userName.toStdString());
cmd.set_should_be_mod(actionClicked == aPromoteToMod);
PendingCommand *pend = client->prepareAdminCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse);
client->sendCommand(pend);
} else if (actionClicked == aPromoteToJudge || actionClicked == aDemoteFromJudge) {
Command_AdjustMod cmd;
cmd.set_user_name(userName.toStdString());
cmd.set_should_be_judge(actionClicked == aPromoteToJudge);
PendingCommand *pend = client->prepareAdminCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::adjustMod_processUserResponse);
client->sendCommand(pend);
} else if (actionClicked == aBanHistory) {
Command_GetBanHistory cmd;
cmd.set_user_name(userName.toStdString());
PendingCommand *pend = client->prepareModeratorCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::banUserHistory_processResponse);
client->sendCommand(pend);
} else if (actionClicked == aWarnUser) {
Command_GetUserInfo cmd;
cmd.set_user_name(userName.toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::warnUser_processUserInfoResponse);
client->sendCommand(pend);
} else if (actionClicked == aWarnHistory) {
Command_GetWarnHistory cmd;
cmd.set_user_name(userName.toStdString());
PendingCommand *pend = client->prepareModeratorCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::warnUserHistory_processResponse);
client->sendCommand(pend);
} else if (actionClicked == aGetAdminNotes) {
Command_GetAdminNotes cmd;
cmd.set_user_name(userName.toStdString());
auto *pend = client->prepareModeratorCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserContextMenu::getAdminNotes_processResponse);
client->sendCommand(pend);
} else if (actionClicked == aCopyToClipBoard) {
QClipboard *clipboard = QGuiApplication::clipboard();
clipboard->setText(deckHash);
} else if (actionClicked == aRemoveMessages) {
chatView->redactMessages(userName, -1);
}
delete menu;
}

View file

@ -0,0 +1,79 @@
/**
* @file user_context_menu.h
* @ingroup Lobby
* @brief TODO: Document this.
*/
#ifndef USER_CONTEXT_MENU_H
#define USER_CONTEXT_MENU_H
#include <QObject>
#include <libcockatrice/network/server/remote/user_level.h>
class AbstractGame;
class UserListProxy;
class AbstractClient;
class ChatView;
class CommandContainer;
class QAction;
class QMenu;
class QPoint;
class Response;
class ServerInfo_User;
class TabSupervisor;
class UserContextMenu : public QObject
{
Q_OBJECT
private:
AbstractClient *client;
TabSupervisor *tabSupervisor;
const UserListProxy *userListProxy;
AbstractGame *game;
QAction *aUserName;
QAction *aDetails;
QAction *aShowGames;
QAction *aChat;
QAction *aAddToBuddyList, *aRemoveFromBuddyList;
QAction *aAddToIgnoreList, *aRemoveFromIgnoreList;
QAction *aKick;
QAction *aBan, *aBanHistory;
QAction *aPromoteToMod, *aDemoteFromMod;
QAction *aPromoteToJudge, *aDemoteFromJudge;
QAction *aWarnUser, *aWarnHistory;
QAction *aGetAdminNotes;
signals:
void openMessageDialog(const QString &userName, bool focus);
private slots:
void banUser_processUserInfoResponse(const Response &resp);
void warnUser_processGetWarningsListResponse(const Response &r);
void warnUser_processUserInfoResponse(const Response &resp);
void banUserHistory_processResponse(const Response &resp);
void warnUserHistory_processResponse(const Response &resp);
void getAdminNotes_processResponse(const Response &resp);
void adjustMod_processUserResponse(const Response &resp, const CommandContainer &commandContainer);
void banUser_dialogFinished();
void warnUser_dialogFinished();
void updateAdminNotes_dialogFinished();
void gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer);
public:
UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *_parent, AbstractGame *_game = 0);
void retranslateUi();
void showContextMenu(const QPoint &pos,
const QString &userName,
UserLevelFlags userLevel,
bool online = true,
int playerId = -1);
void showContextMenu(const QPoint &pos, const QString &userName, UserLevelFlags userLevel, ChatView *chatView);
void showContextMenu(const QPoint &pos,
const QString &userName,
UserLevelFlags userLevel,
bool online,
int playerId,
const QString &deckHash,
ChatView *chatView = nullptr);
};
#endif

View file

@ -0,0 +1,385 @@
#include "user_info_box.h"
#include "../../dialogs/dlg_edit_avatar.h"
#include "../../dialogs/dlg_edit_password.h"
#include "../../dialogs/dlg_edit_user.h"
#include "../../interface/pixel_map_generator.h"
#include "../../interface/widgets/utility/get_text_with_max.h"
#include <QDateTime>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QMessageBox>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/response_get_user_info.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/protocol/pb/session_commands.pb.h>
#include <libcockatrice/protocol/pending_command.h>
#include <libcockatrice/utility/passwordhasher.h>
UserInfoBox::UserInfoBox(AbstractClient *_client, bool _editable, QWidget *parent, Qt::WindowFlags flags)
: QWidget(parent, flags), client(_client), editable(_editable)
{
QFont nameFont = nameLabel.font();
nameFont.setBold(true);
nameFont.setPointSizeF(nameFont.pointSizeF() * 1.5);
nameLabel.setFont(nameFont);
auto *userIconAndNameLayout = new QHBoxLayout;
userIconAndNameLayout->addWidget(&userLevelIcon, 0, Qt::AlignCenter);
userIconAndNameLayout->addWidget(&nameLabel, 1);
userIconAndNameLayout->addStretch();
avatarPic.setMinimumSize(200, 200);
avatarPic.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
avatarPic.setAlignment(Qt::AlignCenter);
auto *avatarLayout = new QHBoxLayout;
avatarLayout->setContentsMargins(0, 0, 0, 0);
avatarLayout->addStretch(1);
avatarLayout->addWidget(&avatarPic, 3);
avatarLayout->addStretch(1);
auto *mainLayout = new QGridLayout;
mainLayout->addLayout(avatarLayout, 0, 0, 1, 3);
mainLayout->addLayout(userIconAndNameLayout, 1, 0, 1, 3);
mainLayout->addWidget(&realNameLabel1, 2, 0, 1, 1);
mainLayout->addWidget(&realNameLabel2, 2, 1, 1, 2);
mainLayout->addWidget(&countryLabel1, 3, 0, 1, 1);
mainLayout->addWidget(&countryLabel2, 3, 1, 1, 1, Qt::AlignCenter);
mainLayout->addWidget(&countryLabel3, 3, 2, 1, 1);
mainLayout->addWidget(&userLevelLabel1, 4, 0, 1, 1);
mainLayout->addWidget(&userLevelLabel2, 4, 1, 1, 2);
mainLayout->addWidget(&accountAgeLabel1, 5, 0, 1, 1);
mainLayout->addWidget(&accountAgeLabel2, 5, 1, 1, 2);
mainLayout->setColumnStretch(2, 10);
if (editable) {
auto *buttonsLayout = new QHBoxLayout;
buttonsLayout->addWidget(&editButton);
buttonsLayout->addWidget(&passwordButton);
buttonsLayout->addWidget(&avatarButton);
mainLayout->addLayout(buttonsLayout, 7, 0, 1, 3);
connect(&editButton, &QPushButton::clicked, this, &UserInfoBox::actEdit);
connect(&passwordButton, &QPushButton::clicked, this, &UserInfoBox::actPassword);
connect(&avatarButton, &QPushButton::clicked, this, &UserInfoBox::actAvatar);
}
setWindowTitle(tr("User Information"));
setLayout(mainLayout);
retranslateUi();
}
void UserInfoBox::retranslateUi()
{
realNameLabel1.setText(tr("Real Name:"));
countryLabel1.setText(tr("Location:"));
userLevelLabel1.setText(tr("User Level:"));
accountAgeLabel1.setText(tr("Account Age:"));
editButton.setText(tr("Edit"));
passwordButton.setText(tr("Change password"));
avatarButton.setText(tr("Change avatar"));
}
/**
* Creates the default profile pic that is used when the user doesn't have a custom pic
*/
static QPixmap createDefaultAvatar(int height, const ServerInfo_User &user)
{
return UserLevelPixmapGenerator::generatePixmap(height, UserLevelFlags(user.user_level()), user.pawn_colors(),
false, QString::fromStdString(user.privlevel()));
}
void UserInfoBox::updateInfo(const ServerInfo_User &user)
{
currentUserInfo = &user;
const UserLevelFlags userLevel(user.user_level());
const std::string &bmp = user.avatar_bmp();
if (!avatarPixmap.loadFromData((const uchar *)bmp.data(), static_cast<uint>(bmp.size()))) {
avatarPixmap = createDefaultAvatar(64, user);
hasAvatar = false;
} else {
hasAvatar = true;
}
nameLabel.setText(QString::fromStdString(user.name()));
realNameLabel2.setText(QString::fromStdString(user.real_name()));
QString country = QString::fromStdString(user.country());
if (country.length() != 0) {
countryLabel2.setPixmap(CountryPixmapGenerator::generatePixmap(13, country));
countryLabel3.setText(QString("%1").arg(country.toUpper()));
} else {
countryLabel2.setText("");
countryLabel3.setText("");
}
userLevelIcon.setPixmap(UserLevelPixmapGenerator::generatePixmap(15, userLevel, user.pawn_colors(), false,
QString::fromStdString(user.privlevel())));
QString userLevelText;
if (userLevel.testFlag(ServerInfo_User::IsAdmin))
userLevelText = tr("Administrator");
else if (userLevel.testFlag(ServerInfo_User::IsModerator))
userLevelText = tr("Moderator");
else if (userLevel.testFlag(ServerInfo_User::IsRegistered))
userLevelText = tr("Registered user");
else
userLevelText = tr("Unregistered user");
if (userLevel.testFlag(ServerInfo_User::IsJudge))
userLevelText += " | " + tr("Judge");
if (user.has_privlevel() && user.privlevel() != "NONE") {
userLevelText += " | " + QString("%1").arg(user.privlevel().c_str());
}
userLevelLabel2.setText(userLevelText);
QString accountAgeString = tr("Unregistered user");
if (userLevel.testFlag(ServerInfo_User::IsAdmin) || userLevel.testFlag(ServerInfo_User::IsModerator) ||
userLevel.testFlag(ServerInfo_User::IsRegistered)) {
accountAgeString = getAgeString(user.accountage_secs());
}
accountAgeLabel2.setText(accountAgeString);
}
QString UserInfoBox::getAgeString(int ageSeconds)
{
QString accountAgeString = tr("Unknown");
if (ageSeconds <= 0)
return accountAgeString;
// secsSinceEpoch is in utc
auto secsSinceEpoch = QDateTime::currentSecsSinceEpoch() - ageSeconds;
// the date is in local time, fromSecsSinceEpoch expects a timestamp from utc and converts it to local time
auto date = QDateTime::fromSecsSinceEpoch(secsSinceEpoch).date();
if (!date.isValid())
return accountAgeString;
// now can be local time as the date is also local time
auto now = QDate::currentDate();
auto daysAndYears = getDaysAndYearsBetween(date, now);
QString dateString = QLocale().toString(date, QLocale::ShortFormat);
QString yearString;
if (daysAndYears.second > 0) {
yearString = tr("%n Year(s), ", "amount of years (only shown if more than 0)", daysAndYears.second);
}
accountAgeString =
tr("%10%n Day(s) %20", "amount of years (if more than 0), amount of days, date in local short format",
daysAndYears.first)
.arg(yearString)
.arg(dateString);
return accountAgeString;
}
void UserInfoBox::updateInfo(const QString &userName)
{
Command_GetUserInfo cmd;
cmd.set_user_name(userName.toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserInfoBox::processResponse);
client->sendCommand(pend);
}
void UserInfoBox::processResponse(const Response &r)
{
const Response_GetUserInfo &response = r.GetExtension(Response_GetUserInfo::ext);
updateInfo(response.user_info());
resize(sizeHint());
show();
}
void UserInfoBox::actEdit()
{
Command_GetUserInfo cmd;
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserInfoBox::actEditInternal);
client->sendCommand(pend);
}
void UserInfoBox::actEditInternal(const Response &r)
{
const Response_GetUserInfo &response = r.GetExtension(Response_GetUserInfo::ext);
const ServerInfo_User &user = response.user_info();
QString email = QString::fromStdString(user.email());
QString country = QString::fromStdString(user.country());
QString realName = QString::fromStdString(user.real_name());
DlgEditUser dlg(this, email, country, realName);
if (!dlg.exec())
return;
Command_AccountEdit cmd;
cmd.set_real_name(dlg.getRealName().toStdString());
if (client->getServerSupportsPasswordHash()) {
if (email != dlg.getEmail()) {
// real password is required to change email
bool ok = false;
QString password =
getTextWithMax(this, tr("Enter Password"),
tr("Password verification is required in order to change your email address"),
QLineEdit::Password, "", &ok);
if (!ok)
return;
cmd.set_password_check(password.toStdString());
cmd.set_email(dlg.getEmail().toStdString());
} // servers that support password hash do not require all fields to be filled anymore
} else {
cmd.set_email(dlg.getEmail().toStdString());
}
cmd.set_country(dlg.getCountry().toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserInfoBox::processEditResponse);
client->sendCommand(pend);
}
void UserInfoBox::actPassword()
{
DlgEditPassword dlg(this);
if (!dlg.exec())
return;
auto oldPassword = dlg.getOldPassword();
auto newPassword = dlg.getNewPassword();
if (client->getServerSupportsPasswordHash()) {
Command_RequestPasswordSalt cmd;
cmd.set_user_name(client->getUserName().toStdString());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, [=, this](const Response &response) {
if (response.response_code() == Response::RespOk) {
changePassword(oldPassword, newPassword);
} else {
QMessageBox::critical(this, tr("Error"),
tr("An error occurred while trying to update your user information."));
}
});
client->sendCommand(pend);
} else {
changePassword(oldPassword, newPassword);
}
}
void UserInfoBox::changePassword(const QString &oldPassword, const QString &newPassword)
{
Command_AccountPassword cmd;
cmd.set_old_password(oldPassword.toStdString());
if (client->getServerSupportsPasswordHash()) {
auto passwordSalt = PasswordHasher::generateRandomSalt();
QString hashedPassword = PasswordHasher::computeHash(newPassword, passwordSalt);
cmd.set_hashed_new_password(hashedPassword.toStdString());
} else {
cmd.set_new_password(newPassword.toStdString());
}
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserInfoBox::processPasswordResponse);
client->sendCommand(pend);
}
void UserInfoBox::actAvatar()
{
DlgEditAvatar dlg(this);
if (!dlg.exec())
return;
Command_AccountImage cmd;
cmd.set_image(dlg.getImage().data(), dlg.getImage().size());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, &PendingCommand::finished, this, &UserInfoBox::processAvatarResponse);
client->sendCommand(pend);
}
void UserInfoBox::processEditResponse(const Response &r)
{
switch (r.response_code()) {
case Response::RespOk:
updateInfo(nameLabel.text());
QMessageBox::information(this, tr("Information"), tr("User information updated."));
break;
case Response::RespFunctionNotAllowed:
QMessageBox::critical(this, tr("Error"),
tr("This server does not permit you to update your user informations."));
break;
case Response::RespWrongPassword:
QMessageBox::critical(this, tr("Error"), tr("The entered password does not match your account."));
break;
case Response::RespInternalError:
default:
QMessageBox::critical(this, tr("Error"),
tr("An error occurred while trying to update your user information."));
break;
}
}
void UserInfoBox::processPasswordResponse(const Response &r)
{
switch (r.response_code()) {
case Response::RespOk:
QMessageBox::information(this, tr("Information"), tr("Password changed."));
break;
case Response::RespFunctionNotAllowed:
QMessageBox::critical(this, tr("Error"), tr("This server does not permit you to change your password."));
break;
case Response::RespPasswordTooShort:
QMessageBox::critical(this, tr("Error"), tr("The new password is too short."));
break;
case Response::RespWrongPassword:
QMessageBox::critical(this, tr("Error"), tr("The old password is incorrect."));
break;
case Response::RespInternalError:
default:
QMessageBox::critical(this, tr("Error"),
tr("An error occurred while trying to update your user information."));
break;
}
}
void UserInfoBox::processAvatarResponse(const Response &r)
{
switch (r.response_code()) {
case Response::RespOk:
updateInfo(nameLabel.text());
QMessageBox::information(this, tr("Information"), tr("Avatar updated."));
break;
case Response::RespFunctionNotAllowed:
QMessageBox::critical(this, tr("Error"), tr("This server does not permit you to update your avatar."));
break;
case Response::RespInternalError:
default:
QMessageBox::critical(this, tr("Error"), tr("An error occured while trying to updater your avatar."));
break;
}
}
void UserInfoBox::resizeEvent(QResizeEvent *event)
{
QPixmap resizedPixmap;
if (hasAvatar) {
resizedPixmap = avatarPixmap.scaled(avatarPic.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
} else {
int height = qMin(avatarPic.size().width(), avatarPic.size().height());
resizedPixmap = createDefaultAvatar(height, *currentUserInfo);
}
avatarPic.setPixmap(resizedPixmap);
QWidget::resizeEvent(event);
}

View file

@ -0,0 +1,63 @@
/**
* @file user_info_box.h
* @ingroup Lobby
* @brief TODO: Document this.
*/
#ifndef USERINFOBOX_H
#define USERINFOBOX_H
#include <QDateTime>
#include <QLabel>
#include <QPushButton>
#include <QWidget>
class ServerInfo_User;
class AbstractClient;
class Response;
class UserInfoBox : public QWidget
{
Q_OBJECT
private:
AbstractClient *client;
bool editable;
QLabel avatarPic, userLevelIcon, nameLabel, realNameLabel1, realNameLabel2, countryLabel1, countryLabel2,
countryLabel3, userLevelLabel1, userLevelLabel2, accountAgeLabel1, accountAgeLabel2;
QPushButton editButton, passwordButton, avatarButton;
QPixmap avatarPixmap;
bool hasAvatar;
const ServerInfo_User *currentUserInfo;
static QString getAgeString(int ageSeconds);
public:
UserInfoBox(AbstractClient *_client, bool editable, QWidget *parent = nullptr, Qt::WindowFlags flags = {});
void retranslateUi();
inline static QPair<int, int> getDaysAndYearsBetween(const QDate &then, const QDate &now)
{
int years = now.addDays(1 - then.dayOfYear()).year() - then.year(); // there is no yearsTo
int days = then.addYears(years).daysTo(now);
return {days, years};
}
private slots:
void processResponse(const Response &r);
void processEditResponse(const Response &r);
void processPasswordResponse(const Response &r);
void processAvatarResponse(const Response &r);
void actEdit();
void actEditInternal(const Response &r);
void actPassword();
void actAvatar();
public slots:
void updateInfo(const ServerInfo_User &user);
void updateInfo(const QString &userName);
private:
void resizeEvent(QResizeEvent *event) override;
void changePassword(const QString &oldPassword, const QString &newPassword);
};
#endif

View file

@ -0,0 +1,79 @@
#include "user_info_connection.h"
#include <QDebug>
#include <libcockatrice/settings/cache_settings.h>
#include <utility>
UserConnection_Information::UserConnection_Information() = default;
UserConnection_Information::UserConnection_Information(QString _saveName,
QString _serverName,
QString _portNum,
QString _userName,
QString _pass,
bool _savePass,
QString _site)
: saveName(std::move(_saveName)), server(std::move(_serverName)), port(std::move(_portNum)),
username(std::move(_userName)), password(std::move(_pass)), savePassword(_savePass), site(std::move(_site))
{
}
QMap<QString, std::pair<QString, UserConnection_Information>> UserConnection_Information::getServerInfo()
{
QMap<QString, std::pair<QString, UserConnection_Information>> serverList;
ServersSettings &servers = SettingsCache::instance().servers();
int size = servers.getValue("totalServers", "server", "server_details").toInt() + 1;
for (int i = 0; i < size; i++) {
QString _saveName = servers.getValue(QString("saveName%1").arg(i), "server", "server_details").toString();
QString serverName = servers.getValue(QString("server%1").arg(i), "server", "server_details").toString();
QString portNum = servers.getValue(QString("port%1").arg(i), "server", "server_details").toString();
QString userName = servers.getValue(QString("username%1").arg(i), "server", "server_details").toString();
QString pass = servers.getValue(QString("password%1").arg(i), "server", "server_details").toString();
bool savePass = servers.getValue(QString("savePassword%1").arg(i), "server", "server_details").toBool();
QString _site = servers.getValue(QString("site%1").arg(i), "server", "server_details").toString();
UserConnection_Information userInfo(_saveName, serverName, portNum, userName, pass, savePass, _site);
serverList.insert(_saveName, std::make_pair(serverName, userInfo));
}
return serverList;
}
QStringList UserConnection_Information::getServerInfo(const QString &find)
{
QStringList _server;
ServersSettings &servers = SettingsCache::instance().servers();
int size = servers.getValue("totalServers", "server", "server_details").toInt() + 1;
for (int i = 0; i < size; i++) {
QString _saveName = servers.getValue(QString("saveName%1").arg(i), "server", "server_details").toString();
if (find != _saveName)
continue;
QString serverName = servers.getValue(QString("server%1").arg(i), "server", "server_details").toString();
QString portNum = servers.getValue(QString("port%1").arg(i), "server", "server_details").toString();
QString userName = servers.getValue(QString("username%1").arg(i), "server", "server_details").toString();
QString pass = servers.getValue(QString("password%1").arg(i), "server", "server_details").toString();
bool savePass = servers.getValue(QString("savePassword%1").arg(i), "server", "server_details").toBool();
QString _site = servers.getValue(QString("site%1").arg(i), "server", "server_details").toString();
_server.append(_saveName);
_server.append(serverName);
_server.append(portNum);
_server.append(userName);
_server.append(pass);
_server.append(savePass ? "1" : "0");
_server.append(_site);
break;
}
if (_server.empty())
qCWarning(UserInfoConnectionLog) << "There was a problem!";
return _server;
}

View file

@ -0,0 +1,64 @@
/**
* @file user_info_connection.h
* @ingroup Client
* @brief TODO: Document this.
*/
#ifndef USERCONNECTION_INFORMATION_H
#define USERCONNECTION_INFORMATION_H
#include <QApplication>
#include <QDir>
#include <QFile>
#include <QLoggingCategory>
#include <QSettings>
#include <QStandardPaths>
inline Q_LOGGING_CATEGORY(UserInfoConnectionLog, "user_info_connection");
class UserConnection_Information
{
private:
QString saveName;
QString server;
QString port;
QString username;
QString password;
bool savePassword;
QString site;
public:
UserConnection_Information();
UserConnection_Information(QString, QString, QString, QString, QString, bool, QString);
QString getSaveName() const
{
return saveName;
}
QString getServer() const
{
return server;
}
QString getPort() const
{
return port;
}
QString getUsername() const
{
return username;
}
QString getPassword() const
{
return password;
}
bool getSavePassword() const
{
return savePassword;
}
QString getSite() const
{
return site;
}
QMap<QString, std::pair<QString, UserConnection_Information>> getServerInfo();
QStringList getServerInfo(const QString &find);
};
#endif

View file

@ -0,0 +1,170 @@
#include "user_list_manager.h"
#include "../../client/sound_engine.h"
#include "user_info_box.h"
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/event_add_to_list.pb.h>
#include <libcockatrice/protocol/pb/event_remove_from_list.pb.h>
#include <libcockatrice/protocol/pb/event_user_joined.pb.h>
#include <libcockatrice/protocol/pb/event_user_left.pb.h>
#include <libcockatrice/protocol/pb/response_list_users.pb.h>
#include <libcockatrice/protocol/pb/session_commands.pb.h>
#include <libcockatrice/protocol/pending_command.h>
UserListManager::UserListManager(AbstractClient *_client, QObject *parent)
: QObject(parent), client(_client), ownUserInfo(nullptr)
{
connect(client, &AbstractClient::userJoinedEventReceived, this, &UserListManager::processUserJoinedEvent);
connect(client, &AbstractClient::userLeftEventReceived, this, &UserListManager::processUserLeftEvent);
connect(client, &AbstractClient::buddyListReceived, this, &UserListManager::buddyListReceived);
connect(client, &AbstractClient::ignoreListReceived, this, &UserListManager::ignoreListReceived);
connect(client, &AbstractClient::addToListEventReceived, this, &UserListManager::processAddToListEvent);
connect(client, &AbstractClient::removeFromListEventReceived, this, &UserListManager::processRemoveFromListEvent);
connect(client, &AbstractClient::userInfoChanged, this, &UserListManager::setOwnUserInfo);
}
UserListManager::~UserListManager()
{
handleDisconnect();
}
void UserListManager::handleConnect()
{
populateInitialOnlineUsers();
}
void UserListManager::handleDisconnect()
{
onlineUsers.clear();
buddyUsers.clear();
ignoredUsers.clear();
delete ownUserInfo;
ownUserInfo = nullptr;
}
void UserListManager::setOwnUserInfo(const ServerInfo_User &userInfo)
{
ownUserInfo = new ServerInfo_User(userInfo);
}
void UserListManager::populateInitialOnlineUsers()
{
PendingCommand *pend = client->prepareSessionCommand(Command_ListUsers());
connect(pend, &PendingCommand::finished, this, &UserListManager::processListUsersResponse);
client->sendCommand(pend);
}
void UserListManager::processListUsersResponse(const Response &response)
{
const auto &resp = response.GetExtension(Response_ListUsers::ext);
const int userListSize = resp.user_list_size();
for (int i = 0; i < userListSize; ++i) {
const ServerInfo_User &info = resp.user_list(i);
const QString &userName = QString::fromStdString(info.name());
onlineUsers.insert(userName, info);
}
}
void UserListManager::processUserJoinedEvent(const Event_UserJoined &event)
{
const auto &info = event.user_info();
const QString &userName = QString::fromStdString(info.name());
onlineUsers.insert(userName, info);
}
void UserListManager::processUserLeftEvent(const Event_UserLeft &event)
{
const auto &userName = QString::fromStdString(event.name());
onlineUsers.remove(userName);
}
void UserListManager::buddyListReceived(const QList<ServerInfo_User> &_buddyList)
{
for (const auto &user : _buddyList) {
const auto &userName = QString::fromStdString(user.name());
buddyUsers.insert(userName, user);
}
}
void UserListManager::ignoreListReceived(const QList<ServerInfo_User> &_ignoreList)
{
for (const auto &user : _ignoreList) {
const auto &userName = QString::fromStdString(user.name());
ignoredUsers.insert(userName, user);
}
}
void UserListManager::processAddToListEvent(const Event_AddToList &event)
{
const auto &user = event.user_info();
const auto &userName = QString::fromStdString(user.name());
const auto &userListType = QString::fromStdString(event.list_name());
QMap<QString, ServerInfo_User> *userMap;
if (userListType == "buddy") {
userMap = &buddyUsers;
} else if (userListType == "ignore") {
userMap = &ignoredUsers;
} else {
return;
}
userMap->insert(userName, user);
}
void UserListManager::processRemoveFromListEvent(const Event_RemoveFromList &event)
{
const auto &userListType = QString::fromStdString(event.list_name());
const auto &userName = QString::fromStdString(event.user_name());
QMap<QString, ServerInfo_User> *userMap;
if (userListType == "buddy") {
userMap = &buddyUsers;
} else if (userListType == "ignore") {
userMap = &ignoredUsers;
} else {
return;
}
userMap->remove(userName);
}
bool UserListManager::isOwnUserRegistered() const
{
return ownUserInfo != nullptr && (ownUserInfo->user_level() & ServerInfo_User::IsRegistered) != 0;
}
QString UserListManager::getOwnUsername() const
{
return ownUserInfo != nullptr ? QString::fromStdString(ownUserInfo->name()) : QString();
}
bool UserListManager::isUserBuddy(const QString &userName) const
{
return buddyUsers.contains(userName);
}
bool UserListManager::isUserIgnored(const QString &userName) const
{
return ignoredUsers.contains(userName);
}
const ServerInfo_User *UserListManager::getOnlineUser(const QString &userName) const
{
const QString &userNameToMatchLower = userName.toLower();
const auto it =
std::find_if(onlineUsers.begin(), onlineUsers.end(), [&userNameToMatchLower](const ServerInfo_User &user) {
return userNameToMatchLower == QString::fromStdString(user.name()).toLower();
});
if (it != onlineUsers.end()) {
return &*it;
}
return nullptr;
};

View file

@ -0,0 +1,78 @@
/**
* @file user_list_manager.h
* @ingroup Lobby
* @brief TODO: Document this.
*/
#ifndef COCKATRICE_USER_LIST_MANAGER_H
#define COCKATRICE_USER_LIST_MANAGER_H
#include "user_list_proxy.h"
#include <QMap>
#include <QWidget>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
class AbstractClient;
class Event_AddToList;
class Event_ListRooms;
class Event_RemoveFromList;
class Event_UserJoined;
class Event_UserLeft;
class Response;
class ServerInfo_User;
class TabSupervisor;
class UserListManager : public QObject, public UserListProxy
{
Q_OBJECT
private:
AbstractClient *client;
ServerInfo_User *ownUserInfo;
QMap<QString, ServerInfo_User> onlineUsers, buddyUsers, ignoredUsers;
private slots:
void setOwnUserInfo(const ServerInfo_User &userInfo);
void populateInitialOnlineUsers();
void processListUsersResponse(const Response &response);
void processUserJoinedEvent(const Event_UserJoined &event);
void processUserLeftEvent(const Event_UserLeft &event);
void buddyListReceived(const QList<ServerInfo_User> &_buddyList);
void ignoreListReceived(const QList<ServerInfo_User> &_ignoreList);
void processAddToListEvent(const Event_AddToList &event);
void processRemoveFromListEvent(const Event_RemoveFromList &event);
public:
explicit UserListManager(AbstractClient *_client, QObject *parent = nullptr);
~UserListManager() override;
[[nodiscard]] QMap<QString, ServerInfo_User> getAllUsersList() const
{
return onlineUsers;
}
[[nodiscard]] QMap<QString, ServerInfo_User> getBuddyList() const
{
return buddyUsers;
}
[[nodiscard]] QMap<QString, ServerInfo_User> getIgnoreList() const
{
return ignoredUsers;
}
bool isOwnUserRegistered() const override;
QString getOwnUsername() const override;
bool isUserBuddy(const QString &userName) const override;
bool isUserIgnored(const QString &userName) const override;
const ServerInfo_User *getOnlineUser(const QString &userName) const override;
public slots:
void handleConnect();
void handleDisconnect();
signals:
void userLeft(const QString &userName);
void userJoined(const ServerInfo_User &userInfo);
};
#endif // COCKATRICE_USER_LIST_MANAGER_H

View file

@ -0,0 +1,28 @@
/**
* @file user_list_proxy.h
* @ingroup UI
* @ingroup Lobby
* @brief TODO: Document this.
*/
#ifndef COCKATRICE_USERLISTPROXY_H
#define COCKATRICE_USERLISTPROXY_H
class QString;
class ServerInfo_User;
/**
* Responsible for providing a bare-bones minimal interface into userlist information,
* including your current connection to the server as well as buddy/ignore/alluser lists.
*/
class UserListProxy
{
public:
virtual bool isOwnUserRegistered() const = 0;
virtual QString getOwnUsername() const = 0;
virtual bool isUserBuddy(const QString &userName) const = 0;
virtual bool isUserIgnored(const QString &userName) const = 0;
virtual const ServerInfo_User *getOnlineUser(const QString &userName) const = 0; // Can return nullptr
};
#endif // COCKATRICE_USERLISTPROXY_H

View file

@ -0,0 +1,539 @@
#include "user_list_widget.h"
#include "../../interface/pixel_map_generator.h"
#include "../../tabs/tab_account.h"
#include "../../tabs/tab_supervisor.h"
#include "../game_selector.h"
#include "user_context_menu.h"
#include "user_list_manager.h"
#include <QApplication>
#include <QCheckBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QRadioButton>
#include <QSpinBox>
#include <QVBoxLayout>
#include <QWidget>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/moderator_commands.pb.h>
#include <libcockatrice/protocol/pb/response_get_games_of_user.pb.h>
#include <libcockatrice/protocol/pb/response_get_user_info.pb.h>
#include <libcockatrice/protocol/pb/session_commands.pb.h>
#include <libcockatrice/protocol/pending_command.h>
#include <libcockatrice/utility/trice_limits.h>
BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent) : QDialog(parent)
{
setAttribute(Qt::WA_DeleteOnClose);
nameBanCheckBox = new QCheckBox(tr("ban &user name"));
nameBanCheckBox->setChecked(true);
nameBanEdit = new QLineEdit(QString::fromStdString(info.name()));
nameBanEdit->setMaxLength(MAX_NAME_LENGTH);
ipBanCheckBox = new QCheckBox(tr("ban &IP address"));
ipBanCheckBox->setChecked(true);
ipBanEdit = new QLineEdit(QString::fromStdString(info.address()));
ipBanEdit->setMaxLength(MAX_NAME_LENGTH);
idBanCheckBox = new QCheckBox(tr("ban client I&D"));
idBanCheckBox->setChecked(true);
idBanEdit = new QLineEdit(QString::fromStdString(info.clientid()));
idBanEdit->setMaxLength(MAX_NAME_LENGTH);
if (QString::fromStdString(info.clientid()).isEmpty())
idBanCheckBox->setChecked(false);
QGridLayout *banTypeGrid = new QGridLayout;
banTypeGrid->addWidget(nameBanCheckBox, 0, 0);
banTypeGrid->addWidget(nameBanEdit, 0, 1);
banTypeGrid->addWidget(ipBanCheckBox, 1, 0);
banTypeGrid->addWidget(ipBanEdit, 1, 1);
banTypeGrid->addWidget(idBanCheckBox, 2, 0);
banTypeGrid->addWidget(idBanEdit, 2, 1);
QGroupBox *banTypeGroupBox = new QGroupBox(tr("Ban type"));
banTypeGroupBox->setLayout(banTypeGrid);
permanentRadio = new QRadioButton(tr("&permanent ban"));
temporaryRadio = new QRadioButton(tr("&temporary ban"));
temporaryRadio->setChecked(true);
connect(temporaryRadio, &QRadioButton::toggled, this, &BanDialog::enableTemporaryEdits);
daysLabel = new QLabel(tr("&Days:"));
daysEdit = new QSpinBox;
daysEdit->setMinimum(0);
daysEdit->setValue(0);
daysEdit->setMaximum(10000);
daysLabel->setBuddy(daysEdit);
hoursLabel = new QLabel(tr("&Hours:"));
hoursEdit = new QSpinBox;
hoursEdit->setMinimum(0);
hoursEdit->setValue(0);
hoursEdit->setMaximum(24);
hoursLabel->setBuddy(hoursEdit);
minutesLabel = new QLabel(tr("&Minutes:"));
minutesEdit = new QSpinBox;
minutesEdit->setMinimum(0);
minutesEdit->setValue(5);
minutesEdit->setMaximum(60);
minutesLabel->setBuddy(minutesEdit);
QGridLayout *durationLayout = new QGridLayout;
durationLayout->addWidget(permanentRadio, 0, 0, 1, 6);
durationLayout->addWidget(temporaryRadio, 1, 0, 1, 6);
durationLayout->addWidget(daysLabel, 2, 0);
durationLayout->addWidget(daysEdit, 2, 1);
durationLayout->addWidget(hoursLabel, 2, 2);
durationLayout->addWidget(hoursEdit, 2, 3);
durationLayout->addWidget(minutesLabel, 2, 4);
durationLayout->addWidget(minutesEdit, 2, 5);
QGroupBox *durationGroupBox = new QGroupBox(tr("Duration of the ban"));
durationGroupBox->setLayout(durationLayout);
QLabel *reasonLabel = new QLabel(tr("Please enter the reason for the ban.\nThis is only saved for moderators and "
"cannot be seen by the banned person."));
reasonEdit = new QPlainTextEdit;
QLabel *visibleReasonLabel =
new QLabel(tr("Please enter the reason for the ban that will be visible to the banned person."));
visibleReasonEdit = new QPlainTextEdit;
deleteMessages = new QCheckBox(tr("Redact all messages from this user in all rooms"));
QPushButton *okButton = new QPushButton(tr("&OK"));
okButton->setAutoDefault(true);
connect(okButton, &QPushButton::clicked, this, &BanDialog::okClicked);
QPushButton *cancelButton = new QPushButton(tr("&Cancel"));
connect(cancelButton, &QPushButton::clicked, this, &BanDialog::reject);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(banTypeGroupBox);
vbox->addWidget(durationGroupBox);
vbox->addWidget(reasonLabel);
vbox->addWidget(reasonEdit);
vbox->addWidget(visibleReasonLabel);
vbox->addWidget(visibleReasonEdit);
vbox->addWidget(deleteMessages);
vbox->addLayout(buttonLayout);
setLayout(vbox);
setWindowTitle(tr("Ban user from server"));
}
WarningDialog::WarningDialog(const QString userName, const QString clientID, QWidget *parent) : QDialog(parent)
{
setAttribute(Qt::WA_DeleteOnClose);
descriptionLabel = new QLabel(tr("Which warning would you like to send?"));
nameWarning = new QLineEdit(userName);
nameWarning->setMaxLength(MAX_NAME_LENGTH);
warnClientID = new QLineEdit(clientID);
warnClientID->setMaxLength(MAX_NAME_LENGTH);
warningOption = new QComboBox();
warningOption->addItem("");
deleteMessages = new QCheckBox(tr("Redact all messages from this user in all rooms"));
QPushButton *okButton = new QPushButton(tr("&OK"));
okButton->setAutoDefault(true);
connect(okButton, &QPushButton::clicked, this, &WarningDialog::okClicked);
QPushButton *cancelButton = new QPushButton(tr("&Cancel"));
connect(cancelButton, &QPushButton::clicked, this, &WarningDialog::reject);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(descriptionLabel);
vbox->addWidget(nameWarning);
vbox->addWidget(warningOption);
vbox->addWidget(deleteMessages);
vbox->addLayout(buttonLayout);
setLayout(vbox);
setWindowTitle(tr("Warn user for misconduct"));
}
void WarningDialog::okClicked()
{
if (nameWarning->text().simplified().isEmpty()) {
QMessageBox::critical(this, tr("Error"),
tr("User name to send a warning to can not be blank, please specify a user to warn."));
return;
}
if (warningOption->currentText().simplified().isEmpty()) {
QMessageBox::critical(this, tr("Error"),
tr("Warning to use can not be blank, please select a valid warning to send."));
return;
}
accept();
}
QString WarningDialog::getName() const
{
return nameWarning->text().simplified();
}
QString WarningDialog::getWarnID() const
{
return warnClientID->text().simplified();
}
QString WarningDialog::getReason() const
{
return warningOption->currentText().simplified();
}
int WarningDialog::getDeleteMessages() const
{
return deleteMessages->isChecked() ? -1 : 0;
}
void WarningDialog::addWarningOption(const QString warning)
{
warningOption->addItem(warning);
}
void BanDialog::okClicked()
{
if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked() && !idBanCheckBox->isChecked()) {
QMessageBox::critical(this, tr("Error"),
tr("You have to select a name-based, IP-based, clientId based, or some combination of "
"the three to place a ban."));
return;
}
if (nameBanCheckBox->isChecked())
if (nameBanEdit->text().simplified() == "") {
QMessageBox::critical(this, tr("Error"),
tr("You must have a value in the name ban when selecting the name ban checkbox."));
return;
}
if (ipBanCheckBox->isChecked())
if (ipBanEdit->text().simplified() == "") {
QMessageBox::critical(this, tr("Error"),
tr("You must have a value in the ip ban when selecting the ip ban checkbox."));
return;
}
if (idBanCheckBox->isChecked())
if (idBanEdit->text().simplified() == "") {
QMessageBox::critical(
this, tr("Error"),
tr("You must have a value in the clientid ban when selecting the clientid ban checkbox."));
return;
}
accept();
}
void BanDialog::enableTemporaryEdits(bool enabled)
{
daysLabel->setEnabled(enabled);
daysEdit->setEnabled(enabled);
hoursLabel->setEnabled(enabled);
hoursEdit->setEnabled(enabled);
minutesLabel->setEnabled(enabled);
minutesEdit->setEnabled(enabled);
}
QString BanDialog::getBanId() const
{
return idBanCheckBox->isChecked() ? idBanEdit->text() : QString();
}
QString BanDialog::getBanName() const
{
return nameBanCheckBox->isChecked() ? nameBanEdit->text() : QString();
}
QString BanDialog::getBanIP() const
{
return ipBanCheckBox->isChecked() ? ipBanEdit->text() : QString();
}
int BanDialog::getMinutes() const
{
return permanentRadio->isChecked() ? 0
: (daysEdit->value() * 24 * 60 + hoursEdit->value() * 60 + minutesEdit->value());
}
QString BanDialog::getReason() const
{
return reasonEdit->toPlainText();
}
QString BanDialog::getVisibleReason() const
{
return visibleReasonEdit->toPlainText();
}
int BanDialog::getDeleteMessages() const
{
return deleteMessages->isChecked() ? -1 : 0;
}
AdminNotesDialog::AdminNotesDialog(const QString &_userName, const QString &_notes, QWidget *_parent)
: QDialog(_parent), userName(_userName)
{
setAttribute(Qt::WA_DeleteOnClose);
auto *updateButton = new QPushButton(tr("Update Notes"));
updateButton->setEnabled(false);
connect(updateButton, &QPushButton::clicked, this, &AdminNotesDialog::accept);
notes = new QPlainTextEdit(_notes);
notes->setMinimumWidth(500);
connect(notes, &QPlainTextEdit::textChanged, this, [=]() { updateButton->setEnabled(true); });
auto *vbox = new QVBoxLayout;
vbox->addWidget(notes);
vbox->addWidget(updateButton);
setLayout(vbox);
setWindowTitle(tr("Admin Notes for %1").arg(_userName));
}
QString AdminNotesDialog::getNotes() const
{
return notes->toPlainText();
}
UserListItemDelegate::UserListItemDelegate(QObject *const parent) : QStyledItemDelegate(parent)
{
}
bool UserListItemDelegate::editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index)
{
if ((event->type() == QEvent::MouseButtonPress) && index.isValid()) {
QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::RightButton) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
static_cast<UserListWidget *>(parent())->showContextMenu(mouseEvent->globalPosition().toPoint(), index);
#else
static_cast<UserListWidget *>(parent())->showContextMenu(mouseEvent->globalPos(), index);
#endif
return true;
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
UserListTWI::UserListTWI(const ServerInfo_User &_userInfo) : QTreeWidgetItem(Type)
{
setUserInfo(_userInfo);
}
void UserListTWI::setUserInfo(const ServerInfo_User &_userInfo)
{
userInfo = _userInfo;
setData(0, Qt::UserRole, userInfo.user_level());
setIcon(0, UserLevelPixmapGenerator::generateIcon(18, UserLevelFlags(userInfo.user_level()), userInfo.pawn_colors(),
false, QString::fromStdString(userInfo.privlevel())));
setIcon(1, QIcon(CountryPixmapGenerator::generatePixmap(18, QString::fromStdString(userInfo.country()))));
setData(2, Qt::UserRole, QString::fromStdString(userInfo.name()));
setData(2, Qt::DisplayRole, QString::fromStdString(userInfo.name()));
setData(3, Qt::InitialSortOrderRole, QString::fromStdString(userInfo.privlevel()));
}
void UserListTWI::setOnline(bool online)
{
setData(0, Qt::UserRole + 1, online);
setData(2, Qt::ForegroundRole, online ? qApp->palette().brush(QPalette::WindowText) : QBrush(Qt::gray));
}
/**
* Sort Users in the following order
* 1) Online Users > Offline Users
* 2) Admins, judge/vip/donator status ignored
* 3) Moderators, judge/vip/donator status ignored
* 4) Judges
* 5) VIPs
* 6) Donators
* 7) Everyone else
* @param other RHS to compare to
* @return Left is less than the Right
*/
bool UserListTWI::operator<(const QTreeWidgetItem &other) const
{
// Sort by online/offline
if (data(0, Qt::UserRole + 1) != other.data(0, Qt::UserRole + 1)) {
return data(0, Qt::UserRole + 1).toBool();
}
const auto &lhsUserLevelFlags = UserLevelFlags(data(0, Qt::UserRole).toInt());
const auto &rhsUserLevelFlags = UserLevelFlags(other.data(0, Qt::UserRole).toInt());
// Admins & Mods need no additional comparison checks, just to see if they're an admin or a moderator
static const QList<ServerInfo_User_UserLevelFlag> userLevelWithNoOtherPrefOrder = {
ServerInfo_User_UserLevelFlag_IsAdmin, ServerInfo_User_UserLevelFlag_IsModerator};
for (const auto &userLevelEntry : userLevelWithNoOtherPrefOrder) {
if (lhsUserLevelFlags.testFlag(userLevelEntry) &&
lhsUserLevelFlags.testFlag(userLevelEntry) == rhsUserLevelFlags.testFlag(userLevelEntry)) {
return QString::localeAwareCompare(data(2, Qt::UserRole).toString(),
other.data(2, Qt::UserRole).toString()) < 0;
} else if (lhsUserLevelFlags.testFlag(userLevelEntry) != rhsUserLevelFlags.testFlag(userLevelEntry)) {
return lhsUserLevelFlags.testFlag(userLevelEntry) > rhsUserLevelFlags.testFlag(userLevelEntry);
}
}
// Judges can be sorted by their additional ranks
static const QList<ServerInfo_User_UserLevelFlag> userLevelOrder = {ServerInfo_User_UserLevelFlag_IsJudge,
ServerInfo_User_UserLevelFlag_IsRegistered,
ServerInfo_User_UserLevelFlag_IsUser};
for (const auto &userLevelEntry : userLevelOrder) {
if (lhsUserLevelFlags.testFlag(userLevelEntry) != rhsUserLevelFlags.testFlag(userLevelEntry)) {
return lhsUserLevelFlags.testFlag(userLevelEntry) > rhsUserLevelFlags.testFlag(userLevelEntry);
}
}
// Sort by VIP > Donator > None
static const QMap<QString, int> privilegeOrder = {{"VIP", 3}, {"DONATOR", 2}, {"NONE", 1}, {"UNKNOWN", 0}};
const auto &lhsUserPrivLevel = privilegeOrder.value(data(3, Qt::InitialSortOrderRole).toString(), 0);
const auto &rhsUserPrivLevel = privilegeOrder.value(other.data(3, Qt::InitialSortOrderRole).toString(), 0);
if (lhsUserPrivLevel != rhsUserPrivLevel) {
return lhsUserPrivLevel > rhsUserPrivLevel;
}
// Sort by name
return QString::localeAwareCompare(data(2, Qt::UserRole).toString(), other.data(2, Qt::UserRole).toString()) < 0;
}
UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
AbstractClient *_client,
UserListType _type,
QWidget *parent)
: QGroupBox(parent), tabSupervisor(_tabSupervisor), client(_client), type(_type), onlineCount(0)
{
itemDelegate = new UserListItemDelegate(this);
userContextMenu = new UserContextMenu(tabSupervisor, this);
connect(userContextMenu, &UserContextMenu::openMessageDialog, this, &UserListWidget::openMessageDialog);
userTree = new QTreeWidget;
userTree->setColumnCount(3);
userTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
userTree->header()->setMinimumSectionSize(0);
userTree->setHeaderHidden(true);
userTree->setRootIsDecorated(false);
userTree->setIconSize(QSize(20, 18));
userTree->setItemDelegate(itemDelegate);
userTree->setAlternatingRowColors(true);
connect(userTree, &QTreeWidget::itemActivated, this, &UserListWidget::userClicked);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(userTree);
setLayout(vbox);
retranslateUi();
}
void UserListWidget::retranslateUi()
{
userContextMenu->retranslateUi();
switch (type) {
case AllUsersList:
titleStr = tr("Users connected to server: %1");
break;
case RoomList:
titleStr = tr("Users in this room: %1");
break;
case BuddyList:
titleStr = tr("Buddies online: %1 / %2");
break;
case IgnoreList:
titleStr = tr("Ignored users online: %1 / %2");
break;
}
updateCount();
}
void UserListWidget::processUserInfo(const ServerInfo_User &user, bool online)
{
const QString userName = QString::fromStdString(user.name());
UserListTWI *item = users.value(userName);
if (item)
item->setUserInfo(user);
else {
item = new UserListTWI(user);
users.insert(userName, item);
userTree->addTopLevelItem(item);
if (online)
++onlineCount;
updateCount();
}
item->setOnline(online);
}
bool UserListWidget::deleteUser(const QString &userName)
{
UserListTWI *twi = users.value(userName);
if (twi) {
users.remove(userName);
userTree->takeTopLevelItem(userTree->indexOfTopLevelItem(twi));
if (twi->data(0, Qt::UserRole + 1).toBool())
--onlineCount;
delete twi;
updateCount();
return true;
}
return false;
}
void UserListWidget::setUserOnline(const QString &userName, bool online)
{
UserListTWI *twi = users.value(userName);
if (!twi)
return;
twi->setOnline(online);
if (online)
++onlineCount;
else
--onlineCount;
updateCount();
}
void UserListWidget::updateCount()
{
QString str = titleStr;
if ((type == BuddyList) || (type == IgnoreList))
str = str.arg(onlineCount);
setTitle(str.arg(userTree->topLevelItemCount()));
}
void UserListWidget::userClicked(QTreeWidgetItem *item, int /*column*/)
{
emit openMessageDialog(item->data(2, Qt::UserRole).toString(), true);
}
void UserListWidget::showContextMenu(const QPoint &pos, const QModelIndex &index)
{
const ServerInfo_User &userInfo = static_cast<UserListTWI *>(userTree->topLevelItem(index.row()))->getUserInfo();
bool online = index.sibling(index.row(), 0).data(Qt::UserRole + 1).toBool();
userContextMenu->showContextMenu(pos, QString::fromStdString(userInfo.name()),
UserLevelFlags(userInfo.user_level()), online);
}
void UserListWidget::sortItems()
{
userTree->sortItems(1, Qt::AscendingOrder);
}

View file

@ -0,0 +1,170 @@
/**
* @file user_list_widget.h
* @ingroup Lobby
* @brief TODO: Document this.
*/
#ifndef USERLIST_H
#define USERLIST_H
#include <QComboBox>
#include <QDialog>
#include <QGroupBox>
#include <QStyledItemDelegate>
#include <QTextEdit>
#include <QTreeWidgetItem>
#include <libcockatrice/network/server/remote/user_level.h>
#include <libcockatrice/protocol/pb/moderator_commands.pb.h>
class QTreeWidget;
class ServerInfo_User;
class AbstractClient;
class TabSupervisor;
class QLabel;
class QCheckBox;
class QSpinBox;
class QRadioButton;
class QPlainTextEdit;
class Response;
class CommandContainer;
class UserContextMenu;
class BanDialog : public QDialog
{
Q_OBJECT
private:
QLabel *daysLabel, *hoursLabel, *minutesLabel;
QCheckBox *nameBanCheckBox, *ipBanCheckBox, *idBanCheckBox, *deleteMessages;
QLineEdit *nameBanEdit, *ipBanEdit, *idBanEdit;
QSpinBox *daysEdit, *hoursEdit, *minutesEdit;
QRadioButton *permanentRadio, *temporaryRadio;
QPlainTextEdit *reasonEdit, *visibleReasonEdit;
private slots:
void okClicked();
void enableTemporaryEdits(bool enabled);
public:
explicit BanDialog(const ServerInfo_User &info, QWidget *parent = nullptr);
QString getBanName() const;
QString getBanIP() const;
QString getBanId() const;
int getMinutes() const;
QString getReason() const;
QString getVisibleReason() const;
int getDeleteMessages() const;
};
class WarningDialog : public QDialog
{
Q_OBJECT
private:
QLabel *descriptionLabel;
QLineEdit *nameWarning;
QComboBox *warningOption;
QLineEdit *warnClientID;
QCheckBox *deleteMessages;
private slots:
void okClicked();
public:
WarningDialog(const QString userName, const QString clientID, QWidget *parent = nullptr);
QString getName() const;
QString getWarnID() const;
QString getReason() const;
int getDeleteMessages() const;
void addWarningOption(const QString warning);
};
class AdminNotesDialog : public QDialog
{
Q_OBJECT
private:
QString userName;
QPlainTextEdit *notes;
public:
explicit AdminNotesDialog(const QString &_userName, const QString &_notes, QWidget *_parent = nullptr);
QString getName() const
{
return userName;
}
QString getNotes() const;
};
class UserListItemDelegate : public QStyledItemDelegate
{
public:
explicit UserListItemDelegate(QObject *const parent);
bool editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index) override;
};
class UserListTWI : public QTreeWidgetItem
{
private:
ServerInfo_User userInfo;
public:
explicit UserListTWI(const ServerInfo_User &_userInfo);
const ServerInfo_User &getUserInfo() const
{
return userInfo;
}
void setUserInfo(const ServerInfo_User &_userInfo);
void setOnline(bool online);
bool operator<(const QTreeWidgetItem &other) const override;
};
class UserListWidget : public QGroupBox
{
Q_OBJECT
public:
enum UserListType
{
AllUsersList,
RoomList,
BuddyList,
IgnoreList
};
private:
QMap<QString, UserListTWI *> users;
TabSupervisor *tabSupervisor;
AbstractClient *client;
UserListType type;
QTreeWidget *userTree;
UserListItemDelegate *itemDelegate;
UserContextMenu *userContextMenu;
int onlineCount;
QString titleStr;
void updateCount();
private slots:
void userClicked(QTreeWidgetItem *item, int column);
signals:
void openMessageDialog(const QString &userName, bool focus);
void addBuddy(const QString &userName);
void removeBuddy(const QString &userName);
void addIgnore(const QString &userName);
void removeIgnore(const QString &userName);
public:
UserListWidget(TabSupervisor *_tabSupervisor,
AbstractClient *_client,
UserListType _type,
QWidget *parent = nullptr);
void retranslateUi();
void processUserInfo(const ServerInfo_User &user, bool online);
bool deleteUser(const QString &userName);
void setUserOnline(const QString &userName, bool online);
const QMap<QString, UserListTWI *> &getUsers() const
{
return users;
}
void showContextMenu(const QPoint &pos, const QModelIndex &index);
void sortItems();
};
#endif