diff --git a/cockatrice/src/chatview.cpp b/cockatrice/src/chatview.cpp index 3b72afe2..c0a7e577 100644 --- a/cockatrice/src/chatview.cpp +++ b/cockatrice/src/chatview.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "chatview.h" #include "user_level.h" #include "user_context_menu.h" @@ -223,6 +224,10 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use cursor.insertText("@" + userName, mentionFormat); message = message.mid(mention.size()); QApplication::alert(this); + if (shouldShowSystemPopup()) { + QString ref = sender.left(sender.length() - 2); + showSystemPopup(ref); + } } else { int mentionEndIndex = message.indexOf(QRegExp("\\W"), 1);// from 1 as @ is non-char if (mentionEndIndex == -1) @@ -251,6 +256,22 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use verticalScrollBar()->setValue(verticalScrollBar()->maximum()); } +void ChatView::actMessageClicked() { + emit messageClickedSignal(); +} + +bool ChatView::shouldShowSystemPopup() { + return tabSupervisor->currentIndex() != tabSupervisor->indexOf(this) || + QApplication::activeWindow() == 0 || QApplication::focusWidget() == 0; +} + +void ChatView::showSystemPopup(QString &sender) { + disconnect(trayIcon, SIGNAL(messageClicked()), 0, 0); + trayIcon->showMessage(sender + tr(" mentioned you."), tr("Click to view")); + connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(actMessageClicked())); +} + + QColor ChatView::getCustomMentionColor() { QColor customColor; customColor.setNamedColor("#" + settingsCache->getChatMentionColor()); diff --git a/cockatrice/src/chatview.h b/cockatrice/src/chatview.h index 4589fb26..4fec655a 100644 --- a/cockatrice/src/chatview.h +++ b/cockatrice/src/chatview.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "userlist.h" #include "user_level.h" #include "tab_supervisor.h" @@ -32,14 +33,18 @@ private: bool showTimestamps; HoveredItemType hoveredItemType; QString hoveredContent; + QAction *messageClicked; QTextFragment getFragmentUnderMouse(const QPoint &pos) const; QTextCursor prepareBlock(bool same = false); void appendCardTag(QTextCursor &cursor, const QString &cardName); void appendUrlTag(QTextCursor &cursor, QString url); QString getNameFromUserList(QMap &userList, QString &userName); QColor getCustomMentionColor(); + bool shouldShowSystemPopup(); + void showSystemPopup(QString &sender); private slots: void openLink(const QUrl &link); + void actMessageClicked(); public: ChatView(const TabSupervisor *_tabSupervisor, TabGame *_game, bool _showTimestamps, QWidget *parent = 0); void retranslateUi(); @@ -59,6 +64,7 @@ signals: void showCardInfoPopup(QPoint pos, QString cardName); void deleteCardInfoPopup(QString cardName); void addMentionTag(QString mentionTag); + void messageClickedSignal(); }; #endif diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index fcc8d8ef..1807cb4c 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -598,6 +598,12 @@ MessagesSettingsPage::MessagesSettingsPage() updateMentionPreview(); connect(mentionColor, SIGNAL(textChanged(QString)), this, SLOT(updateColor(QString))); + messagePopups.setChecked(settingsCache->getShowMessagePopup()); + connect(&messagePopups, SIGNAL(stateChanged(int)), settingsCache, SLOT(setShowMessagePopups(int))); + + mentionPopups.setChecked(settingsCache->getShowMentionPopup()); + connect(&mentionPopups, SIGNAL(stateChanged(int)), settingsCache, SLOT(setShowMentionPopups(int))); + QGridLayout *chatGrid = new QGridLayout; chatGrid->addWidget(&chatMentionCheckBox, 0, 0); chatGrid->addWidget(&invertMentionForeground, 0, 1); @@ -605,6 +611,8 @@ MessagesSettingsPage::MessagesSettingsPage() chatGrid->addWidget(&ignoreUnregUsersMainChat, 1, 0); chatGrid->addWidget(&hexLabel, 1, 2); chatGrid->addWidget(&ignoreUnregUserMessages, 2, 0); + chatGrid->addWidget(&messagePopups, 3, 0); + chatGrid->addWidget(&mentionPopups, 4, 0); chatGroupBox = new QGroupBox; chatGroupBox->setLayout(chatGrid); @@ -699,6 +707,8 @@ void MessagesSettingsPage::retranslateUi() ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users.")); ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users.")); invertMentionForeground.setText(tr("Invert text color")); + messagePopups.setText(tr("Enable desktop notifications for private messages.")); + mentionPopups.setText(tr("Enable desktop notification for mentions.")); hexLabel.setText(tr("(Color is hexadecimal)")); } diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 1acda925..64814e87 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -169,6 +169,8 @@ private: QCheckBox invertMentionForeground; QCheckBox ignoreUnregUsersMainChat; QCheckBox ignoreUnregUserMessages; + QCheckBox messagePopups; + QCheckBox mentionPopups; QGroupBox *chatGroupBox; QGroupBox *messageShortcuts; QLineEdit *mentionColor; diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 7e0eb3fa..b21cc374 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "main.h" #include "window_main.h" @@ -48,6 +49,8 @@ QTranslator *translator, *qtTranslator; SettingsCache *settingsCache; RNG_Abstract *rng; SoundEngine *soundEngine; +QSystemTrayIcon *trayIcon; + const QString translationPrefix = "cockatrice"; #ifdef TRANSLATION_PATH diff --git a/cockatrice/src/main.h b/cockatrice/src/main.h index 9237f23c..dc10de84 100644 --- a/cockatrice/src/main.h +++ b/cockatrice/src/main.h @@ -3,10 +3,12 @@ class CardDatabase; class QTranslator; +class QSystemTrayIcon; class SoundEngine; extern CardDatabase *db; +extern QSystemTrayIcon *trayIcon; extern QTranslator *translator; extern const QString translationPrefix; extern QString translationPath; diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 105afa70..552c1b81 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -75,6 +75,8 @@ SettingsCache::SettingsCache() attemptAutoConnect = settings->value("server/auto_connect", 0).toBool(); scaleCards = settings->value("cards/scaleCards", true).toBool(); + showMessagePopups = settings->value("chat/showmessagepopups", true).toBool(); + showMentionPopups = settings->value("chat/showmentionpopups", true).toBool(); } void SettingsCache::setCardScaling(const int _scaleCards) { @@ -82,6 +84,16 @@ void SettingsCache::setCardScaling(const int _scaleCards) { settings->setValue("cards/scaleCards", scaleCards); } +void SettingsCache::setShowMessagePopups(const int _showMessagePopups) { + showMessagePopups = _showMessagePopups; + settings->setValue("chat/showmessagepopups", showMessagePopups); +} + +void SettingsCache::setShowMentionPopups(const int _showMentionPopus) { + showMentionPopups = _showMentionPopus; + settings->setValue("chat/showmentionpopups", showMentionPopups); +} + void SettingsCache::setLang(const QString &_lang) { lang = _lang; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 587bafa1..4bad12de 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -75,6 +75,8 @@ private: bool attemptAutoConnect; int pixmapCacheSize; bool scaleCards; + bool showMessagePopups; + bool showMentionPopups; public: SettingsCache(); const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; } @@ -127,6 +129,8 @@ public: bool getAutoConnect() const { return attemptAutoConnect; } int getPixmapCacheSize() const { return pixmapCacheSize; } bool getScaleCards() const { return scaleCards; } + bool getShowMessagePopup() const { return showMessagePopups; } + bool getShowMentionPopup() const { return showMentionPopups; } public slots: void setMainWindowGeometry(const QByteArray &_mainWindowGeometry); void setLang(const QString &_lang); @@ -172,6 +176,8 @@ public slots: void setAutoConnect(const bool &_autoConnect); void setPixmapCacheSize(const int _pixmapCacheSize); void setCardScaling(const int _scaleCards); + void setShowMessagePopups(const int _showMessagePopups); + void setShowMentionPopups(const int _showMentionPopups); }; extern SettingsCache *settingsCache; diff --git a/cockatrice/src/tab_message.cpp b/cockatrice/src/tab_message.cpp index 1c55d8d5..fbfaf699 100644 --- a/cockatrice/src/tab_message.cpp +++ b/cockatrice/src/tab_message.cpp @@ -6,6 +6,10 @@ #include "tab_message.h" #include "abstractclient.h" #include "chatview.h" +#include "main.h" +#include "settingscache.h" +#include +#include #include "pending_command.h" #include "pb/session_commands.pb.h" @@ -107,9 +111,29 @@ void TabMessage::processUserMessageEvent(const Event_UserMessage &event) { const UserLevelFlags userLevel(event.sender_name() == otherUserInfo->name() ? otherUserInfo->user_level() : ownUserInfo->user_level()); chatView->appendMessage(QString::fromStdString(event.message()), QString::fromStdString(event.sender_name()), userLevel, true); + if (settingsCache->getShowMessagePopup() && shouldShowSystemPopup(event)) + showSystemPopup(event); + emit userEvent(); } +bool TabMessage::shouldShowSystemPopup(const Event_UserMessage &event) { + return (event.sender_name() == otherUserInfo->name() && + tabSupervisor->currentIndex() != tabSupervisor->indexOf(this)) || + QApplication::activeWindow() == 0 || QApplication::focusWidget() == 0; +} + +void TabMessage::showSystemPopup(const Event_UserMessage &event) { + disconnect(trayIcon, SIGNAL(messageClicked()), 0, 0); + trayIcon->showMessage(tr("Private message from ") + otherUserInfo->name().c_str(), event.message().c_str()); + connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked())); +} + +void TabMessage::messageClicked() { + tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this)); + QApplication::setActiveWindow(this); +} + void TabMessage::processUserLeft() { chatView->appendMessage(tr("%1 has left the server.").arg(QString::fromStdString(otherUserInfo->name()))); diff --git a/cockatrice/src/tab_message.h b/cockatrice/src/tab_message.h index 9f2b19d2..b6fe1f03 100644 --- a/cockatrice/src/tab_message.h +++ b/cockatrice/src/tab_message.h @@ -30,6 +30,7 @@ private slots: void actLeave(); void messageSent(const Response &response); void addMentionTag(QString mentionTag); + void messageClicked(); public: TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User &_ownUserInfo, const ServerInfo_User &_otherUserInfo); ~TabMessage(); @@ -40,8 +41,12 @@ public: QString getTabText() const; void processUserMessageEvent(const Event_UserMessage &event); + void processUserLeft(); void processUserJoined(const ServerInfo_User &_userInfo); +private: + bool shouldShowSystemPopup(const Event_UserMessage &event); + void showSystemPopup(const Event_UserMessage &event); }; #endif diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index d0f435c1..06600b5b 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "tab_supervisor.h" #include "tab_room.h" #include "tab_userlists.h" @@ -42,6 +43,7 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool))); chatView = new ChatView(tabSupervisor, 0, true); + connect(chatView, SIGNAL(messageClickedSignal()), this, SLOT(focusTab())); connect(chatView, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool))); connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); connect(chatView, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString))); @@ -125,6 +127,11 @@ void TabRoom::retranslateUi() aOpenChatSettings->setText(tr("Chat Settings...")); } +void TabRoom::focusTab() { + QApplication::setActiveWindow(this); + tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this)); +} + void TabRoom::closeRequest() { actLeaveRoom(); diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index 115093fd..4783b11f 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -56,6 +56,7 @@ private slots: void actClearChat(); void actOpenChatSettings(); void addMentionTag(QString mentionTag); + void focusTab(); void processListGamesEvent(const Event_ListGames &event); void processJoinRoomEvent(const Event_JoinRoom &event); diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 7f1c16f1..1f5743e0 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "main.h" #include "window_main.h" @@ -413,6 +415,11 @@ MainWindow::MainWindow(QWidget *parent) resize(900, 700); restoreGeometry(settingsCache->getMainWindowGeometry()); aFullScreen->setChecked(windowState() & Qt::WindowFullScreen); + + if (QSystemTrayIcon::isSystemTrayAvailable()) { + createTrayActions(); + createTrayIcon(); + } } MainWindow::~MainWindow() @@ -421,6 +428,37 @@ MainWindow::~MainWindow() clientThread->wait(); } +void MainWindow::createTrayIcon() { + QMenu *trayIconMenu = new QMenu(this); + trayIconMenu->addAction(closeAction); + + trayIcon = new QSystemTrayIcon(this); + trayIcon->setContextMenu(trayIconMenu); + trayIcon->setIcon(QIcon(":/resources/appicon.svg")); + trayIcon->show(); + + connect(trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this, + SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); +} + +void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::DoubleClick) { + if (windowState() != Qt::WindowMinimized) + showMinimized(); + else { + showNormal(); + QApplication::setActiveWindow(this); + } + } +} + + +void MainWindow::createTrayActions() { + closeAction = new QAction(tr("&Exit"), this); + connect(closeAction, SIGNAL(triggered()), this, SLOT(close())); +} + + void MainWindow::closeEvent(QCloseEvent *event) { // workaround Qt bug where closeEvent gets called twice diff --git a/cockatrice/src/window_main.h b/cockatrice/src/window_main.h index 8e2b48e2..f8799ccf 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -21,6 +21,7 @@ #define WINDOW_H #include +#include #include "abstractclient.h" #include "pb/response.pb.h" @@ -56,18 +57,28 @@ private slots: void actExit(); void actAbout(); + + void iconActivated(QSystemTrayIcon::ActivationReason reason); private: static const QString appName; void setClientStatusTitle(); void retranslateUi(); void createActions(); void createMenus(); + + void createTrayIcon(); + void createTrayActions(); + QList tabMenus; QMenu *cockatriceMenu, *helpMenu; QAction *aConnect, *aDisconnect, *aSinglePlayer, *aWatchReplay, *aDeckEditor, *aFullScreen, *aSettings, *aExit, *aAbout; TabSupervisor *tabSupervisor; + QMenu *trayIconMenu; + + QAction *closeAction; + RemoteClient *client; QThread *clientThread; diff --git a/cockatrice/translations/cockatrice_en.ts b/cockatrice/translations/cockatrice_en.ts index e655f96a..36014c73 100644 --- a/cockatrice/translations/cockatrice_en.ts +++ b/cockatrice/translations/cockatrice_en.ts @@ -555,6 +555,19 @@ This is only saved for moderators and cannot be seen by the banned person. + + ChatView + + + mentioned you. + + + + + Click to view + + + DBPriceUpdater @@ -1092,19 +1105,19 @@ This is only saved for moderators and cannot be seen by the banned person. DlgSettings - - - + + + Error - + Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1115,7 +1128,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -1126,7 +1139,7 @@ Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Daenyth/Cockatrice/issues with your cards.xml attached @@ -1135,21 +1148,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Daenyth/Cockatrice/issues @@ -1158,42 +1171,42 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings - + General - + Appearance - + User interface - + Deck editor - + Chat Settings @@ -1487,54 +1500,54 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. - + Scheduled server shutdown. - + Banned by moderator - + Expected end time: %1 - + This ban lasts indefinitely. - - + + Invalid username. - + Connection closed - + The server has terminated your connection. Reason: %1 - + Scheduled server shutdown - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1544,240 +1557,241 @@ Reason for shutdown: %1 - + Number of players - + Please enter the number of players. - - + + Player %1 - + Load replay - + About Cockatrice - + Version %1 - + Authors: - + Translators: - + Spanish: - + Portugese (Portugal): - + Portugese (Brazil): - + French: - + Japanese: - + Korean: - + Russian: - + Italian: - + Swedish: - - - - - - - - - + + + + + + + + + Error - + Server timeout - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. - + You are banned until %1. - + You are banned indefinitely. - + This server requires user registration. - + Unknown login error: %1 - + Socket error: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. - + Connecting to %1... - + Disconnected - + Connected, logging in at %1 - + Logged in as %1 at %2 - + &Connect... - + &Disconnect - + Start &local game... - + &Watch replay... - + &Deck editor - + &Full screen - + Ctrl+F - + &Settings... - + + &Exit - + A&ctions - + &Cockatrice - + &About Cockatrice - + &Help @@ -2990,62 +3004,72 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + &Add - + &Remove - + Chat settings - + Enable chat mentions - + In-game message macros - + Ignore unregistered users in main chat - + Ignore chat room messages sent by unregistered users. - + Ignore private messages sent by unregistered users. - + Invert text color - + + Enable desktop notifications for private messages. + + + + + Enable desktop notification for mentions. + + + + (Color is hexadecimal) - + Add message - + Message: @@ -3657,37 +3681,37 @@ Local version is %1, remote version is %2. QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3695,7 +3719,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) @@ -4337,17 +4361,22 @@ Please enter a name: - + + You are flooding the game. Please wait a couple of seconds. + + + + You have been kicked out of the game. - + Replay %1: %2 - + Game %1: %2 @@ -4355,32 +4384,37 @@ Please enter a name: TabMessage - + Private &chat - + &Leave - + %1 - Private chat - + This user is ignoring you. - + + Private message from + + + + %1 has left the server. - + %1 has joined the server. @@ -4448,42 +4482,42 @@ Please enter a name: TabRoom - + F12 - + &Say: - + Chat - + &Room - + &Leave room - + &Clear chat - + Chat Settings... - + You are flooding the chat. Please wait a couple of seconds.