diff --git a/src/mumble/CMakeLists.txt b/src/mumble/CMakeLists.txt index 7fc604127..87bee30c5 100644 --- a/src/mumble/CMakeLists.txt +++ b/src/mumble/CMakeLists.txt @@ -209,6 +209,9 @@ set(MUMBLE_SOURCES "UserInformation.ui" "UserListModel.cpp" "UserListModel.h" + "UserLocalNicknameDialog.cpp" + "UserLocalNicknameDialog.h" + "UserLocalNicknameDialog.ui" "UserLocalVolumeDialog.cpp" "UserLocalVolumeDialog.h" "UserLocalVolumeDialog.ui" diff --git a/src/mumble/ClientUser.cpp b/src/mumble/ClientUser.cpp index 221cf36bd..6655cf76e 100644 --- a/src/mumble/ClientUser.cpp +++ b/src/mumble/ClientUser.cpp @@ -24,6 +24,10 @@ float ClientUser::getLocalVolumeAdjustments() const { return m_localVolume; } +QString ClientUser::getLocalNickname() const { + return m_localNickname; +} + ClientUser *ClientUser::get(unsigned int uiSession) { QReadLocker lock(&c_qrwlUsers); ClientUser *p = c_qmUsers.value(uiSession); @@ -240,6 +244,14 @@ void ClientUser::setLocalVolumeAdjustment(float adjustment) { emit localVolumeAdjustmentsChanged(m_localVolume, oldAdjustment); } +void ClientUser::setLocalNickname(const QString &nickname) { + if (m_localNickname != nickname) { + m_localNickname = nickname; + + emit localNicknameChanged(); + } +} + bool ClientUser::lessThanOverlay(const ClientUser *first, const ClientUser *second) { if (g.s.os.osSort == OverlaySettings::LastStateChange) { // Talkers above non-talkers diff --git a/src/mumble/ClientUser.h b/src/mumble/ClientUser.h index 4b2ddab5f..ec080c63b 100644 --- a/src/mumble/ClientUser.h +++ b/src/mumble/ClientUser.h @@ -20,6 +20,7 @@ private: protected: float m_localVolume = 1.0f; + QString m_localNickname; public: Settings::TalkState tsState; @@ -42,6 +43,8 @@ public: float getLocalVolumeAdjustments() const; + QString getLocalNickname() const; + /** * Determines whether a user is active or not * A user is active when it is currently speaking or when the user has @@ -81,12 +84,14 @@ public slots: void setPrioritySpeaker(bool priority); void setRecording(bool recording); void setLocalVolumeAdjustment(float adjustment); + void setLocalNickname(const QString &nickname); signals: void talkingStateChanged(); void muteDeafStateChanged(); void prioritySpeakerStateChanged(); void recordingStateChanged(); void localVolumeAdjustmentsChanged(float newAdjustment, float oldAdjustment); + void localNicknameChanged(); }; #endif diff --git a/src/mumble/Database.cpp b/src/mumble/Database.cpp index 48b52988c..10f40d407 100644 --- a/src/mumble/Database.cpp +++ b/src/mumble/Database.cpp @@ -207,6 +207,9 @@ Database::Database(const QString &dbname) { execQueryAndLogFailure(query, QLatin1String("CREATE TABLE IF NOT EXISTS `volume` (`id` INTEGER PRIMARY KEY " "AUTOINCREMENT, `hash` TEXT, `volume` FLOAT)")); execQueryAndLogFailure(query, QLatin1String("CREATE UNIQUE INDEX IF NOT EXISTS `volume_hash` ON `volume`(`hash`)")); + execQueryAndLogFailure(query, QLatin1String("CREATE TABLE IF NOT EXISTS `nicknames` (`id` INTEGER PRIMARY KEY " + "AUTOINCREMENT, `hash` TEXT, `nickname` TEXT)")); + execQueryAndLogFailure(query, QLatin1String("CREATE UNIQUE INDEX IF NOT EXISTS `nicknames_hash` ON `nicknames`(`hash`)")); // Note: A previous snapshot version created a table called 'hidden' execQueryAndLogFailure( @@ -365,6 +368,27 @@ float Database::getUserLocalVolume(const QString &hash) { return 1.0f; } +void Database::setUserLocalNickname(const QString &hash, const QString &nickname) { + QSqlQuery query(db); + + query.prepare(QLatin1String("INSERT OR REPLACE INTO `nicknames` (`hash`, `nickname`) VALUES (?,?)")); + query.addBindValue(hash); + query.addBindValue(nickname); + execQueryAndLogFailure(query); +} + +QString Database::getUserLocalNickname(const QString &hash) { + QSqlQuery query(db); + + query.prepare(QLatin1String("SELECT `nickname` FROM `nicknames` WHERE `hash` = ?")); + query.addBindValue(hash); + execQueryAndLogFailure(query); + if (query.first()) { + return query.value(0).toString(); + } + return QString(); +} + void Database::setLocalMuted(const QString &hash, bool muted) { QSqlQuery query(db); diff --git a/src/mumble/Database.h b/src/mumble/Database.h index e139053a8..4bed1914d 100644 --- a/src/mumble/Database.h +++ b/src/mumble/Database.h @@ -51,6 +51,9 @@ public: float getUserLocalVolume(const QString &hash); void setUserLocalVolume(const QString &hash, float volume); + QString getUserLocalNickname(const QString &hash); + void setUserLocalNickname(const QString &hash, const QString &nickname); + bool isChannelFiltered(const QByteArray &server_cert_digest, const int channel_id); void setChannelFiltered(const QByteArray &server_cert_digest, const int channel_id, bool hidden); diff --git a/src/mumble/LookConfig.cpp b/src/mumble/LookConfig.cpp index 71c7164d8..0b72abbe6 100644 --- a/src/mumble/LookConfig.cpp +++ b/src/mumble/LookConfig.cpp @@ -182,6 +182,7 @@ void LookConfig::load(const Settings &r) { loadCheckBox(qcbStateInTray, r.bStateInTray); loadCheckBox(qcbShowUserCount, r.bShowUserCount); loadCheckBox(qcbShowVolumeAdjustments, r.bShowVolumeAdjustments); + loadCheckBox(qcbShowNicknamesOnly, r.bShowNicknamesOnly); loadCheckBox(qcbShowContextMenuInMenuBar, r.bShowContextMenuInMenuBar); loadCheckBox(qcbShowTransmitModeComboBox, r.bShowTransmitModeComboBox); loadCheckBox(qcbHighContrast, r.bHighContrast); @@ -244,6 +245,7 @@ void LookConfig::save() const { s.bStateInTray = qcbStateInTray->isChecked(); s.bShowUserCount = qcbShowUserCount->isChecked(); s.bShowVolumeAdjustments = qcbShowVolumeAdjustments->isChecked(); + s.bShowNicknamesOnly = qcbShowNicknamesOnly->isChecked(); s.bShowContextMenuInMenuBar = qcbShowContextMenuInMenuBar->isChecked(); s.bShowTransmitModeComboBox = qcbShowTransmitModeComboBox->isChecked(); s.bHighContrast = qcbHighContrast->isChecked(); diff --git a/src/mumble/LookConfig.ui b/src/mumble/LookConfig.ui index 6b2cc1c57..bd804382d 100644 --- a/src/mumble/LookConfig.ui +++ b/src/mumble/LookConfig.ui @@ -681,7 +681,7 @@ Channel Tree - + Filter automatically hides empty channels @@ -722,7 +722,7 @@ - + Use selected item as the chat bar target @@ -779,6 +779,16 @@ + + + + Hide the username for each user if they have a nickname. + + + Show nicknames only + + + diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp index 89904bca8..cd5a17bfb 100644 --- a/src/mumble/MainWindow.cpp +++ b/src/mumble/MainWindow.cpp @@ -48,6 +48,7 @@ #include "User.h" #include "UserEdit.h" #include "UserInformation.h" +#include "UserLocalNicknameDialog.h" #include "UserLocalVolumeDialog.h" #include "UserModel.h" #include "Utils.h" @@ -1600,6 +1601,7 @@ void MainWindow::qmUser_aboutToShow() { if (g.s.bTTS) qmUser->addAction(qaUserLocalIgnoreTTS); qmUser->addAction(qaUserLocalVolume); + qmUser->addAction(qaUserLocalNickname); if (isSelf) qmUser->addAction(qaSelfComment); @@ -1656,6 +1658,7 @@ void MainWindow::qmUser_aboutToShow() { qaUserKick->setEnabled(false); qaUserBan->setEnabled(false); qaUserTextMessage->setEnabled(false); + qaUserLocalNickname->setEnabled(false); qaUserLocalMute->setEnabled(false); qaUserLocalVolume->setEnabled(false); qaUserLocalIgnore->setEnabled(false); @@ -1667,6 +1670,7 @@ void MainWindow::qmUser_aboutToShow() { qaUserKick->setEnabled(!isSelf); qaUserBan->setEnabled(!isSelf); qaUserTextMessage->setEnabled(true); + qaUserLocalNickname->setEnabled(!isSelf); qaUserLocalMute->setEnabled(!isSelf); qaUserLocalVolume->setEnabled(!isSelf); qaUserLocalIgnore->setEnabled(!isSelf); @@ -1969,6 +1973,20 @@ void MainWindow::openTextMessageDialog(ClientUser *p) { delete texm; } +void MainWindow::on_qaUserLocalNickname_triggered() { + ClientUser *p = getContextMenuUser(); + + if (!p) + return; + + openUserLocalNicknameDialog(*p); +} + +void MainWindow::openUserLocalNicknameDialog(const ClientUser &p) { + unsigned int session = p.uiSession; + UserLocalNicknameDialog::present(session, qmUserNicknameTracker); +} + void MainWindow::on_qaUserCommentView_triggered() { ClientUser *p = getContextMenuUser(); // This has to be done here because UserModel could've set it. diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h index 109568bfa..8d04221b9 100644 --- a/src/mumble/MainWindow.h +++ b/src/mumble/MainWindow.h @@ -18,6 +18,7 @@ #include "Mumble.pb.h" #include "Usage.h" #include "UserLocalVolumeDialog.h" +#include "UserLocalNicknameDialog.h" #include "ui_MainWindow.h" @@ -69,6 +70,7 @@ public: qiIconMuteSuppressed; QIcon qiTalkingOn, qiTalkingWhisper, qiTalkingShout, qiTalkingOff; QMap< unsigned int, UserLocalVolumeDialog * > qmUserVolTracker; + std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > qmUserNicknameTracker; /// "Action" for when there are no actions available QAction *qaEmpty; @@ -119,6 +121,7 @@ public: void updateChatBar(); void openTextMessageDialog(ClientUser *p); + void openUserLocalNicknameDialog(const ClientUser &p); void openUserLocalVolumeDialog(ClientUser *p); #ifdef Q_OS_WIN @@ -210,6 +213,7 @@ public slots: void on_qaUserLocalIgnore_triggered(); void on_qaUserLocalIgnoreTTS_triggered(); void on_qaUserLocalMute_triggered(); + void on_qaUserLocalNickname_triggered(); void on_qaUserLocalVolume_triggered(); void on_qaUserTextMessage_triggered(); void on_qaUserRegister_triggered(); diff --git a/src/mumble/MainWindow.ui b/src/mumble/MainWindow.ui index e5891e264..9b520b2de 100644 --- a/src/mumble/MainWindow.ui +++ b/src/mumble/MainWindow.ui @@ -378,6 +378,17 @@ Sends a text message to another user. + + + &Set Nickname... + + + Set a local nickname + + + Sets a local nickname for another user. + + &Add... diff --git a/src/mumble/Messages.cpp b/src/mumble/Messages.cpp index f41335f19..204c7c507 100644 --- a/src/mumble/Messages.cpp +++ b/src/mumble/Messages.cpp @@ -553,6 +553,7 @@ void MainWindow::msgUserState(const MumbleProto::UserState &msg) { if (g.db->isLocalIgnoredTTS(pDst->qsHash)) pDst->setLocalIgnoreTTS(true); pDst->setLocalVolumeAdjustment(g.db->getUserLocalVolume(pDst->qsHash)); + pDst->setLocalNickname(g.db->getUserLocalNickname(pDst->qsHash)); } if (msg.has_self_deaf() || msg.has_self_mute()) { diff --git a/src/mumble/Settings.cpp b/src/mumble/Settings.cpp index dfc0bf786..fbb802424 100644 --- a/src/mumble/Settings.cpp +++ b/src/mumble/Settings.cpp @@ -379,6 +379,7 @@ Settings::Settings() { bUsage = true; bShowUserCount = false; bShowVolumeAdjustments = true; + bShowNicknamesOnly = false; bChatBarUseSelection = false; bFilterHidesEmptyChannels = true; bFilterActive = false; @@ -867,6 +868,7 @@ void Settings::load(QSettings *settings_ptr) { SAVELOAD(bUsage, "ui/usage"); SAVELOAD(bShowUserCount, "ui/showusercount"); SAVELOAD(bShowVolumeAdjustments, "ui/showVolumeAdjustments"); + SAVELOAD(bShowNicknamesOnly, "ui/showNicknamesOnly"); SAVELOAD(bChatBarUseSelection, "ui/chatbaruseselection"); SAVELOAD(bFilterHidesEmptyChannels, "ui/filterhidesemptychannels"); SAVELOAD(bFilterActive, "ui/filteractive"); @@ -1237,6 +1239,7 @@ void Settings::save() { SAVELOAD(bUsage, "ui/usage"); SAVELOAD(bShowUserCount, "ui/showusercount"); SAVELOAD(bShowVolumeAdjustments, "ui/showVolumeAdjustments"); + SAVELOAD(bShowNicknamesOnly, "ui/showNicknamesOnly"); SAVELOAD(bChatBarUseSelection, "ui/chatbaruseselection"); SAVELOAD(bFilterHidesEmptyChannels, "ui/filterhidesemptychannels"); SAVELOAD(bFilterActive, "ui/filteractive"); diff --git a/src/mumble/Settings.h b/src/mumble/Settings.h index ee7de014e..7dd399dc0 100644 --- a/src/mumble/Settings.h +++ b/src/mumble/Settings.h @@ -335,6 +335,7 @@ struct Settings { bool bUsage; bool bShowUserCount; bool bShowVolumeAdjustments; + bool bShowNicknamesOnly; bool bChatBarUseSelection; bool bFilterHidesEmptyChannels; bool bFilterActive; diff --git a/src/mumble/UserLocalNicknameDialog.cpp b/src/mumble/UserLocalNicknameDialog.cpp new file mode 100644 index 000000000..afb43bc88 --- /dev/null +++ b/src/mumble/UserLocalNicknameDialog.cpp @@ -0,0 +1,93 @@ +// Copyright 2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "UserLocalNicknameDialog.h" +#include "ClientUser.h" +#include "Database.h" +#include "MainWindow.h" + +#include +#include + +// We define a global macro called 'g'. This can lead to issues when included code uses 'g' as a type or parameter name +// (like protobuf 3.7 does). As such, for now, we have to make this our last include. +#include "Global.h" + +UserLocalNicknameDialog::UserLocalNicknameDialog( + unsigned int sessionId, + std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker) + : QDialog(nullptr), m_clientSession(sessionId), m_qmUserNicknameTracker(qmUserNicknameTracker) { + setupUi(this); + + qleUserLocalNickname->setFocus(); + + qleUserLocalNickname->setAccessibleName(tr("User nickname")); + + ClientUser *user = ClientUser::get(sessionId); + if (!user) { + UserLocalNicknameDialog::close(); + } else { + QString title = tr("Adjusting local nickname for %1").arg(user->qsName); + setWindowTitle(title); + qleUserLocalNickname->setText(user->getLocalNickname()); + m_originalNickname = qleUserLocalNickname->text(); + } + + if (g.mw && g.mw->windowFlags() & Qt::WindowStaysOnTopHint) { + // If the main window is set to always be on top of other windows, we should make the + // nickname dialog behave the same in order for it to not get hidden behind the main window. + setWindowFlags(Qt::WindowStaysOnTopHint); + } +} + +void UserLocalNicknameDialog::closeEvent(QCloseEvent *event) { + m_qmUserNicknameTracker.erase(m_clientSession); + event->accept(); +} + +void UserLocalNicknameDialog::present( + unsigned int sessionId, + std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker) { + if (qmUserNicknameTracker.find(sessionId) != qmUserNicknameTracker.end()) { + qmUserNicknameTracker.at(sessionId)->show(); + qmUserNicknameTracker.at(sessionId)->raise(); + } else { + std::unique_ptr< UserLocalNicknameDialog > userNickname = + std::make_unique< UserLocalNicknameDialog >(sessionId, qmUserNicknameTracker); + userNickname->show(); + qmUserNicknameTracker.insert(std::make_pair(sessionId, std::move(userNickname))); + } +} + +void UserLocalNicknameDialog::on_qleUserLocalNickname_textChanged(const QString &text) { + ClientUser *user = ClientUser::get(m_clientSession); + if (user) { + user->setLocalNickname(text); + } +} + +void UserLocalNicknameDialog::on_qbbUserLocalNickname_clicked(QAbstractButton *button) { + if (button == qbbUserLocalNickname->button(QDialogButtonBox::Reset)) { + qleUserLocalNickname->setText(m_originalNickname); + } else if (button == qbbUserLocalNickname->button(QDialogButtonBox::Ok)) { + ClientUser *user = ClientUser::get(m_clientSession); + if (user) { + if (!user->qsHash.isEmpty()) { + g.db->setUserLocalNickname(user->qsHash, user->getLocalNickname()); + } else { + g.mw->logChangeNotPermanent(QObject::tr("Local Nickname Adjustment..."), user); + } + } + UserLocalNicknameDialog::close(); + } else if (button == qbbUserLocalNickname->button(QDialogButtonBox::Cancel)) { + qleUserLocalNickname->setText(m_originalNickname); + UserLocalNicknameDialog::close(); + } +} + +void UserLocalNicknameDialog::reject() { + m_qmUserNicknameTracker.erase(m_clientSession); + UserLocalNicknameDialog::close(); +} diff --git a/src/mumble/UserLocalNicknameDialog.h b/src/mumble/UserLocalNicknameDialog.h new file mode 100644 index 000000000..7655135f6 --- /dev/null +++ b/src/mumble/UserLocalNicknameDialog.h @@ -0,0 +1,41 @@ +// Copyright 2020 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef MUMBLE_MUMBLE_USERNICKNAME_H_ +#define MUMBLE_MUMBLE_USERNICKNAME_H_ + +#include +#include + +#include "ClientUser.h" +#include "ui_UserLocalNicknameDialog.h" + +class UserLocalNicknameDialog : public QDialog, private Ui::UserLocalNicknameDialog { + Q_OBJECT + Q_DISABLE_COPY(UserLocalNicknameDialog); + + /// The session ID for the user that the dialog is changing the nickname for. + unsigned int m_clientSession; + + /// The user's original nickname when entering the dialog. + QString m_originalNickname; + std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &m_qmUserNicknameTracker; + +public slots: + void closeEvent(QCloseEvent *event); + void on_qleUserLocalNickname_textChanged(const QString &text); + void on_qbbUserLocalNickname_clicked(QAbstractButton *b); + void reject(); + +public: + static void + present(unsigned int sessionId, + std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker); + UserLocalNicknameDialog( + unsigned int sessionId, + std::unordered_map< unsigned int, std::unique_ptr< UserLocalNicknameDialog > > &qmUserNicknameTracker); +}; + +#endif diff --git a/src/mumble/UserLocalNicknameDialog.ui b/src/mumble/UserLocalNicknameDialog.ui new file mode 100644 index 000000000..b75b1c594 --- /dev/null +++ b/src/mumble/UserLocalNicknameDialog.ui @@ -0,0 +1,53 @@ + + + UserLocalNicknameDialog + + + + 0 + 0 + 500 + 80 + + + + + 500 + 80 + + + + + 16777215 + 224 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset + + + false + + + + + + + Local nickname for other users + + + <b>Adjust the nickname of other users locally</b><br /> + + + + + + + + diff --git a/src/mumble/UserModel.cpp b/src/mumble/UserModel.cpp index 764eaa447..31487ea38 100644 --- a/src/mumble/UserModel.cpp +++ b/src/mumble/UserModel.cpp @@ -1019,6 +1019,7 @@ ClientUser *UserModel::addUser(unsigned int id, const QString &name) { connect(p, SIGNAL(prioritySpeakerStateChanged()), this, SLOT(userStateChanged())); connect(p, SIGNAL(recordingStateChanged()), this, SLOT(userStateChanged())); connect(p, &ClientUser::localVolumeAdjustmentsChanged, this, &UserModel::userStateChanged); + connect(p, &ClientUser::localNicknameChanged, this, &UserModel::userStateChanged); Channel *c = Channel::get(0); ModelItem *citem = ModelItem::c_qhChannels.value(c); @@ -1959,18 +1960,30 @@ QString UserModel::createDisplayString(const ClientUser &user, bool isChannelLis friendTag = QString::fromLatin1("(%2)").arg(user.qsFriendName); } + // Create a nickname-tag + QString nickname = user.getLocalNickname(); + // Create a tag that indicates the volume adjustments QString volumeTag; if (std::abs(localVolumeDecibel) > 0 && g.s.bShowVolumeAdjustments) { volumeTag = QString::asprintf("|%+d|", localVolumeDecibel); } - QString displayString = user.qsName; + QString displayString; + if (!g.s.bShowNicknamesOnly || nickname.isEmpty()) { + displayString += user.qsName; + } else { + displayString += nickname; + } if (!friendTag.isEmpty()) { displayString += " " + friendTag; } + if (!g.s.bShowNicknamesOnly && !nickname.isEmpty() && user.qsName.compare(nickname, Qt::CaseInsensitive) != 0) { + displayString += " " + QString::fromLatin1("[%1]").arg(nickname); + } + if (!volumeTag.isEmpty()) { displayString += " " + volumeTag; }