improved banning; added [url] and [card] tags for chat

This commit is contained in:
Max-Wilhelm Bruker 2011-06-25 21:21:19 +02:00
parent 4b84168bda
commit 05ebb83ba4
36 changed files with 2501 additions and 2073 deletions

View file

@ -136,6 +136,7 @@ SOURCES += src/abstractcounter.cpp \
src/gamescene.cpp \ src/gamescene.cpp \
src/arrowitem.cpp \ src/arrowitem.cpp \
src/arrowtarget.cpp \ src/arrowtarget.cpp \
src/tab.cpp \
src/tab_server.cpp \ src/tab_server.cpp \
src/tab_room.cpp \ src/tab_room.cpp \
src/tab_message.cpp \ src/tab_message.cpp \

View file

@ -2,34 +2,41 @@
#include <QDateTime> #include <QDateTime>
#include <QTextTable> #include <QTextTable>
#include <QScrollBar> #include <QScrollBar>
#include <QMouseEvent>
#include <QDesktopServices>
#include "chatview.h" #include "chatview.h"
ChatView::ChatView(const QString &_ownName, QWidget *parent) ChatView::ChatView(const QString &_ownName, bool _showTimestamps, QWidget *parent)
: QTextEdit(parent), ownName(_ownName) : QTextBrowser(parent), ownName(_ownName), showTimestamps(_showTimestamps)
{ {
setTextInteractionFlags(Qt::TextSelectableByMouse); setReadOnly(true);
setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
setOpenLinks(false);
connect(this, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(openLink(const QUrl &)));
} }
void ChatView::appendMessage(QString sender, const QString &message) void ChatView::appendMessage(QString sender, QString message)
{ {
QTextCursor cursor(document()->lastBlock()); QTextCursor cursor(document()->lastBlock());
cursor.movePosition(QTextCursor::End); cursor.movePosition(QTextCursor::End);
QTextBlockFormat blockFormat; QTextBlockFormat blockFormat;
blockFormat.setBottomMargin(3); blockFormat.setBottomMargin(2);
cursor.insertBlock(blockFormat); cursor.insertBlock(blockFormat);
QTextCharFormat timeFormat; if (showTimestamps) {
timeFormat.setForeground(Qt::black); QTextCharFormat timeFormat;
cursor.setCharFormat(timeFormat); timeFormat.setForeground(Qt::black);
cursor.insertText(QDateTime::currentDateTime().toString("[hh:mm] ")); cursor.setCharFormat(timeFormat);
cursor.insertText(QDateTime::currentDateTime().toString("[hh:mm] "));
}
QTextCharFormat senderFormat; QTextCharFormat senderFormat;
if (sender == ownName) { if (sender == ownName) {
senderFormat.setFontWeight(QFont::Bold); senderFormat.setFontWeight(QFont::Bold);
senderFormat.setForeground(Qt::red); senderFormat.setForeground(Qt::red);
} else } else
senderFormat.setForeground(Qt::blue); senderFormat.setForeground(QColor(0, 0, 254));
cursor.setCharFormat(senderFormat); cursor.setCharFormat(senderFormat);
if (!sender.isEmpty()) if (!sender.isEmpty())
sender.append(": "); sender.append(": ");
@ -39,7 +46,122 @@ void ChatView::appendMessage(QString sender, const QString &message)
if (sender.isEmpty()) if (sender.isEmpty())
messageFormat.setForeground(Qt::darkGreen); messageFormat.setForeground(Qt::darkGreen);
cursor.setCharFormat(messageFormat); cursor.setCharFormat(messageFormat);
cursor.insertText(message);
int from = 0, index = 0;
while ((index = message.indexOf('[', from)) != -1) {
cursor.insertText(message.left(index));
message = message.mid(index);
if (message.isEmpty())
break;
if (message.startsWith("[card]")) {
message = message.mid(6);
QTextCharFormat tempFormat = messageFormat;
tempFormat.setForeground(Qt::blue);
cursor.setCharFormat(tempFormat);
int closeTagIndex = message.indexOf("[/card]");
cursor.insertText(message.left(closeTagIndex));
cursor.setCharFormat(messageFormat);
if (closeTagIndex == -1)
message.clear();
else
message = message.mid(closeTagIndex + 7);
} else if (message.startsWith("[url]")) {
message = message.mid(5);
int closeTagIndex = message.indexOf("[/url]");
QString url = message.left(closeTagIndex);
if (!url.startsWith("http://"))
url.prepend("http://");
QTextCharFormat tempFormat = messageFormat;
tempFormat.setForeground(QColor(0, 0, 254));
tempFormat.setAnchor(true);
tempFormat.setAnchorHref(url);
cursor.setCharFormat(tempFormat);
cursor.insertText(url);
cursor.setCharFormat(messageFormat);
if (closeTagIndex == -1)
message.clear();
else
message = message.mid(closeTagIndex + 6);
} else
from = 1;
}
if (!message.isEmpty())
cursor.insertText(message);
verticalScrollBar()->setValue(verticalScrollBar()->maximum()); verticalScrollBar()->setValue(verticalScrollBar()->maximum());
} }
void ChatView::enterEvent(QEvent * /*event*/)
{
setMouseTracking(true);
}
void ChatView::leaveEvent(QEvent * /*event*/)
{
setMouseTracking(false);
}
QTextFragment ChatView::getFragmentUnderMouse(const QPoint &pos) const
{
QTextCursor cursor(cursorForPosition(pos));
QTextBlock block(cursor.block());
QTextBlock::iterator it;
for (it = block.begin(); !(it.atEnd()); ++it) {
QTextFragment frag = it.fragment();
if (frag.contains(cursor.position()))
return frag;
}
return QTextFragment();
}
QString ChatView::getCardNameUnderMouse(QTextFragment frag) const
{
if (frag.charFormat().foreground().color() == Qt::blue)
return frag.text();
return QString();
}
QString ChatView::getCardNameUnderMouse(const QPoint &pos) const
{
return getCardNameUnderMouse(getFragmentUnderMouse(pos));
}
void ChatView::mouseMoveEvent(QMouseEvent *event)
{
QTextFragment frag = getFragmentUnderMouse(event->pos());
QString cardName = getCardNameUnderMouse(frag);
if (!cardName.isEmpty()) {
viewport()->setCursor(Qt::PointingHandCursor);
emit cardNameHovered(cardName);
} else if (frag.charFormat().isAnchor())
viewport()->setCursor(Qt::PointingHandCursor);
else
viewport()->setCursor(Qt::IBeamCursor);
QTextBrowser::mouseMoveEvent(event);
}
void ChatView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::MidButton) {
QString cardName = getCardNameUnderMouse(event->pos());
if (!cardName.isEmpty())
emit showCardInfoPopup(event->globalPos(), cardName);
}
QTextBrowser::mousePressEvent(event);
}
void ChatView::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::MidButton)
emit deleteCardInfoPopup();
QTextBrowser::mouseReleaseEvent(event);
}
void ChatView::openLink(const QUrl &link)
{
QDesktopServices::openUrl(link);
}

View file

@ -1,18 +1,36 @@
#ifndef CHATVIEW_H #ifndef CHATVIEW_H
#define CHATVIEW_H #define CHATVIEW_H
#include <QTextEdit> #include <QTextBrowser>
#include <QTextFragment>
class QTextTable; class QTextTable;
class QMouseEvent;
class ChatView : public QTextEdit { class ChatView : public QTextBrowser {
Q_OBJECT; Q_OBJECT;
private: private:
QTextTable *table; QTextTable *table;
QString ownName; QString ownName;
bool showTimestamps;
QTextFragment getFragmentUnderMouse(const QPoint &pos) const;
QString getCardNameUnderMouse(QTextFragment frag) const;
QString getCardNameUnderMouse(const QPoint &pos) const;
private slots:
void openLink(const QUrl &link);
public: public:
ChatView(const QString &_ownName, QWidget *parent = 0); ChatView(const QString &_ownName, bool _showTimestamps, QWidget *parent = 0);
void appendMessage(QString sender, const QString &message); void appendMessage(QString sender, QString message);
protected:
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
signals:
void cardNameHovered(QString cardName);
void showCardInfoPopup(QPoint pos, QString cardName);
void deleteCardInfoPopup();
}; };
#endif #endif

View file

@ -11,7 +11,7 @@ class LocalServer : public Server
public: public:
LocalServer(QObject *parent = 0); LocalServer(QObject *parent = 0);
~LocalServer(); ~LocalServer();
AuthenticationResult checkUserPassword(const QString & /*user*/, const QString & /*password*/) { return UnknownUser; } AuthenticationResult checkUserPassword(Server_ProtocolHandler * /*handler*/, const QString & /*user*/, const QString & /*password*/) { return UnknownUser; }
QString getLoginMessage() const { return QString(); } QString getLoginMessage() const { return QString(); }
bool getGameShouldPing() const { return false; } bool getGameShouldPing() const { return false; }
int getMaxGameInactivityTime() const { return 9999999; } int getMaxGameInactivityTime() const { return 9999999; }

View file

@ -1,11 +1,9 @@
#include "messagelogwidget.h" #include "messagelogwidget.h"
#include "player.h" #include "player.h"
#include "cardzone.h" #include "cardzone.h"
#include "cardinfowidget.h"
#include "protocol_items.h" #include "protocol_items.h"
#include "soundengine.h" #include "soundengine.h"
#include <QMouseEvent> #include <QScrollBar>
#include <QTextBlock>
QString MessageLogWidget::sanitizeHtml(QString dirty) const QString MessageLogWidget::sanitizeHtml(QString dirty) const
{ {
@ -15,6 +13,20 @@ QString MessageLogWidget::sanitizeHtml(QString dirty) const
.replace(">", "&gt;"); .replace(">", "&gt;");
} }
void MessageLogWidget::myAppend(const QString &message)
{
QTextCursor cursor(document()->lastBlock());
cursor.movePosition(QTextCursor::End);
QTextBlockFormat blockFormat;
blockFormat.setBottomMargin(2);
cursor.insertBlock(blockFormat);
cursor.insertHtml(message);
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
}
bool MessageLogWidget::isFemale(Player *player) const bool MessageLogWidget::isFemale(Player *player) const
{ {
return player->getUserInfo()->getGender() == ServerInfo_User::Female; return player->getUserInfo()->getGender() == ServerInfo_User::Female;
@ -22,129 +34,129 @@ bool MessageLogWidget::isFemale(Player *player) const
void MessageLogWidget::logConnecting(QString hostname) void MessageLogWidget::logConnecting(QString hostname)
{ {
append(tr("Connecting to %1...").arg(sanitizeHtml(hostname))); myAppend(tr("Connecting to %1...").arg(sanitizeHtml(hostname)));
} }
void MessageLogWidget::logConnected() void MessageLogWidget::logConnected()
{ {
append(tr("Connected.")); myAppend(tr("Connected."));
} }
void MessageLogWidget::logDisconnected() void MessageLogWidget::logDisconnected()
{ {
append(tr("Disconnected from server.")); myAppend(tr("Disconnected from server."));
} }
void MessageLogWidget::logSocketError(const QString &errorString) void MessageLogWidget::logSocketError(const QString &errorString)
{ {
append(sanitizeHtml(errorString)); myAppend(sanitizeHtml(errorString));
} }
void MessageLogWidget::logServerError(ResponseCode response) void MessageLogWidget::logServerError(ResponseCode response)
{ {
switch (response) { switch (response) {
case RespWrongPassword: append(tr("Invalid password.")); break; case RespWrongPassword: myAppend(tr("Invalid password.")); break;
default: ; default: ;
} }
} }
void MessageLogWidget::logProtocolVersionMismatch(int clientVersion, int serverVersion) void MessageLogWidget::logProtocolVersionMismatch(int clientVersion, int serverVersion)
{ {
append(tr("Protocol version mismatch. Client: %1, Server: %2").arg(clientVersion).arg(serverVersion)); myAppend(tr("Protocol version mismatch. Client: %1, Server: %2").arg(clientVersion).arg(serverVersion));
} }
void MessageLogWidget::logProtocolError() void MessageLogWidget::logProtocolError()
{ {
append(tr("Protocol error.")); myAppend(tr("Protocol error."));
} }
void MessageLogWidget::logGameJoined(int gameId) void MessageLogWidget::logGameJoined(int gameId)
{ {
append(tr("You have joined game #%1.").arg(gameId)); myAppend(tr("You have joined game #%1.").arg(gameId));
} }
void MessageLogWidget::logJoin(Player *player) void MessageLogWidget::logJoin(Player *player)
{ {
soundEngine->notification(); soundEngine->notification();
append(tr("%1 has joined the game.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 has joined the game.").arg(sanitizeHtml(player->getName())));
} }
void MessageLogWidget::logLeave(Player *player) void MessageLogWidget::logLeave(Player *player)
{ {
append(tr("%1 has left the game.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 has left the game.").arg(sanitizeHtml(player->getName())));
} }
void MessageLogWidget::logGameClosed() void MessageLogWidget::logGameClosed()
{ {
append(tr("The game has been closed.")); myAppend(tr("The game has been closed."));
} }
void MessageLogWidget::logJoinSpectator(QString name) void MessageLogWidget::logJoinSpectator(QString name)
{ {
append(tr("%1 is now watching the game.").arg(sanitizeHtml(name))); myAppend(tr("%1 is now watching the game.").arg(sanitizeHtml(name)));
} }
void MessageLogWidget::logLeaveSpectator(QString name) void MessageLogWidget::logLeaveSpectator(QString name)
{ {
append(tr("%1 is not watching the game any more.").arg(sanitizeHtml(name))); myAppend(tr("%1 is not watching the game any more.").arg(sanitizeHtml(name)));
} }
void MessageLogWidget::logDeckSelect(Player *player, int deckId) void MessageLogWidget::logDeckSelect(Player *player, int deckId)
{ {
if (deckId == -1) if (deckId == -1)
append(tr("%1 has loaded a local deck.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 has loaded a local deck.").arg(sanitizeHtml(player->getName())));
else else
append(tr("%1 has loaded deck #%2.").arg(sanitizeHtml(player->getName())).arg(deckId)); myAppend(tr("%1 has loaded deck #%2.").arg(sanitizeHtml(player->getName())).arg(deckId));
} }
void MessageLogWidget::logReadyStart(Player *player) void MessageLogWidget::logReadyStart(Player *player)
{ {
append(tr("%1 is ready to start the game.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 is ready to start the game.").arg(sanitizeHtml(player->getName())));
} }
void MessageLogWidget::logNotReadyStart(Player *player) void MessageLogWidget::logNotReadyStart(Player *player)
{ {
append(tr("%1 is not ready to start the game any more.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 is not ready to start the game any more.").arg(sanitizeHtml(player->getName())));
} }
void MessageLogWidget::logConcede(Player *player) void MessageLogWidget::logConcede(Player *player)
{ {
append(tr("%1 has conceded the game.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 has conceded the game.").arg(sanitizeHtml(player->getName())));
} }
void MessageLogWidget::logGameStart() void MessageLogWidget::logGameStart()
{ {
append(tr("The game has started.")); myAppend(tr("The game has started."));
} }
void MessageLogWidget::logConnectionStateChanged(Player *player, bool connectionState) void MessageLogWidget::logConnectionStateChanged(Player *player, bool connectionState)
{ {
if (connectionState) if (connectionState)
append(tr("%1 has restored connection to the game.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 has restored connection to the game.").arg(sanitizeHtml(player->getName())));
else else
append(tr("%1 has lost connection to the game.").arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 has lost connection to the game.").arg(sanitizeHtml(player->getName())));
} }
void MessageLogWidget::logSay(Player *player, QString message) void MessageLogWidget::logSay(Player *player, QString message)
{ {
append(QString("<b><font color=\"") + (player->getLocal() ? "red" : "#0000fe") + QString("\">%1:</font></b> %2").arg(sanitizeHtml(player->getName())).arg(sanitizeHtml(message))); appendMessage(player->getName(), message);
} }
void MessageLogWidget::logSpectatorSay(QString spectatorName, QString message) void MessageLogWidget::logSpectatorSay(QString spectatorName, QString message)
{ {
append(QString("<font color=\"red\">%1:</font> %2").arg(sanitizeHtml(spectatorName)).arg(sanitizeHtml(message))); myAppend(QString("<font color=\"red\">%1:</font> %2").arg(sanitizeHtml(spectatorName)).arg(sanitizeHtml(message)));
} }
void MessageLogWidget::logShuffle(Player *player, CardZone *zone) void MessageLogWidget::logShuffle(Player *player, CardZone *zone)
{ {
soundEngine->shuffle(); soundEngine->shuffle();
if (currentContext != MessageContext_Mulligan) if (currentContext != MessageContext_Mulligan)
append(tr("%1 shuffles %2.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(true, CaseAccusative))); myAppend(tr("%1 shuffles %2.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(true, CaseAccusative)));
} }
void MessageLogWidget::logRollDie(Player *player, int sides, int roll) void MessageLogWidget::logRollDie(Player *player, int sides, int roll)
{ {
append(tr("%1 rolls a %2 with a %3-sided die.").arg(sanitizeHtml(player->getName())).arg(roll).arg(sides)); myAppend(tr("%1 rolls a %2 with a %3-sided die.").arg(sanitizeHtml(player->getName())).arg(roll).arg(sides));
} }
void MessageLogWidget::logDrawCards(Player *player, int number) void MessageLogWidget::logDrawCards(Player *player, int number)
@ -153,16 +165,16 @@ void MessageLogWidget::logDrawCards(Player *player, int number)
mulliganPlayer = player; mulliganPlayer = player;
else { else {
soundEngine->draw(); soundEngine->draw();
append(tr("%1 draws %n card(s).", "", number).arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 draws %n card(s).", "", number).arg(sanitizeHtml(player->getName())));
} }
} }
void MessageLogWidget::logUndoDraw(Player *player, QString cardName) void MessageLogWidget::logUndoDraw(Player *player, QString cardName)
{ {
if (cardName.isEmpty()) if (cardName.isEmpty())
append((isFemale(player) ? tr("%1 undoes her last draw.") : tr("%1 undoes his last draw.")).arg(sanitizeHtml(player->getName()))); myAppend((isFemale(player) ? tr("%1 undoes her last draw.") : tr("%1 undoes his last draw.")).arg(sanitizeHtml(player->getName())));
else else
append((isFemale(player) ? tr("%1 undoes her last draw (%2).") : tr("%1 undoes his last draw (%2).")).arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName)))); myAppend((isFemale(player) ? tr("%1 undoes her last draw (%2).") : tr("%1 undoes his last draw (%2).")).arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName))));
} }
QPair<QString, QString> MessageLogWidget::getFromStr(CardZone *zone, QString cardName, int position) const QPair<QString, QString> MessageLogWidget::getFromStr(CardZone *zone, QString cardName, int position) const
@ -227,7 +239,7 @@ void MessageLogWidget::doMoveCard(LogMoveCard &attributes)
cardStr = QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName)); cardStr = QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName));
if (attributes.startZone->getPlayer() != attributes.targetZone->getPlayer()) { if (attributes.startZone->getPlayer() != attributes.targetZone->getPlayer()) {
append(tr("%1 gives %2 control over %3.").arg(sanitizeHtml(attributes.player->getName())).arg(sanitizeHtml(attributes.targetZone->getPlayer()->getName())).arg(cardStr)); myAppend(tr("%1 gives %2 control over %3.").arg(sanitizeHtml(attributes.player->getName())).arg(sanitizeHtml(attributes.targetZone->getPlayer()->getName())).arg(cardStr));
return; return;
} }
@ -260,7 +272,7 @@ void MessageLogWidget::doMoveCard(LogMoveCard &attributes)
finalStr = tr("%1 plays %2%3."); finalStr = tr("%1 plays %2%3.");
} }
append(finalStr.arg(sanitizeHtml(attributes.player->getName())).arg(cardStr).arg(fromStr).arg(attributes.newX)); myAppend(finalStr.arg(sanitizeHtml(attributes.player->getName())).arg(cardStr).arg(fromStr).arg(attributes.newX));
} }
void MessageLogWidget::logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX) void MessageLogWidget::logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX)
@ -280,50 +292,50 @@ void MessageLogWidget::logMulligan(Player *player, int number)
return; return;
if (number > -1) if (number > -1)
append(tr("%1 takes a mulligan to %n.", "", number).arg(sanitizeHtml(player->getName()))); myAppend(tr("%1 takes a mulligan to %n.", "", number).arg(sanitizeHtml(player->getName())));
else else
append((isFemale(player) ? tr("%1 draws her initial hand.") : tr("%1 draws his initial hand.")).arg(sanitizeHtml(player->getName()))); myAppend((isFemale(player) ? tr("%1 draws her initial hand.") : tr("%1 draws his initial hand.")).arg(sanitizeHtml(player->getName())));
} }
void MessageLogWidget::logFlipCard(Player *player, QString cardName, bool faceDown) void MessageLogWidget::logFlipCard(Player *player, QString cardName, bool faceDown)
{ {
if (faceDown) if (faceDown)
append(tr("%1 flips %2 face-down.").arg(sanitizeHtml(player->getName())).arg(cardName)); myAppend(tr("%1 flips %2 face-down.").arg(sanitizeHtml(player->getName())).arg(cardName));
else else
append(tr("%1 flips %2 face-up.").arg(sanitizeHtml(player->getName())).arg(cardName)); myAppend(tr("%1 flips %2 face-up.").arg(sanitizeHtml(player->getName())).arg(cardName));
} }
void MessageLogWidget::logDestroyCard(Player *player, QString cardName) void MessageLogWidget::logDestroyCard(Player *player, QString cardName)
{ {
append(tr("%1 destroys %2.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName)))); myAppend(tr("%1 destroys %2.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName))));
} }
void MessageLogWidget::logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName) void MessageLogWidget::logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName)
{ {
append(tr("%1 attaches %2 to %3's %4.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName))).arg(sanitizeHtml(targetPlayer->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(targetCardName)))); myAppend(tr("%1 attaches %2 to %3's %4.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName))).arg(sanitizeHtml(targetPlayer->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(targetCardName))));
} }
void MessageLogWidget::logUnattachCard(Player *player, QString cardName) void MessageLogWidget::logUnattachCard(Player *player, QString cardName)
{ {
append(tr("%1 unattaches %2.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName)))); myAppend(tr("%1 unattaches %2.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName))));
} }
void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt) void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt)
{ {
append(tr("%1 creates token: %2%3.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\"><a name=\"foo\">%1</a></font>").arg(sanitizeHtml(cardName))).arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt)))); myAppend(tr("%1 creates token: %2%3.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\"><a name=\"foo\">%1</a></font>").arg(sanitizeHtml(cardName))).arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
} }
void MessageLogWidget::logCreateArrow(Player *player, Player *startPlayer, QString startCard, Player *targetPlayer, QString targetCard, bool playerTarget) void MessageLogWidget::logCreateArrow(Player *player, Player *startPlayer, QString startCard, Player *targetPlayer, QString targetCard, bool playerTarget)
{ {
if (playerTarget) if (playerTarget)
append(tr("%1 points from %2's %3 to %4.") myAppend(tr("%1 points from %2's %3 to %4.")
.arg(sanitizeHtml(player->getName())) .arg(sanitizeHtml(player->getName()))
.arg(sanitizeHtml(startPlayer->getName())) .arg(sanitizeHtml(startPlayer->getName()))
.arg(sanitizeHtml(startCard)) .arg(sanitizeHtml(startCard))
.arg(sanitizeHtml(targetPlayer->getName())) .arg(sanitizeHtml(targetPlayer->getName()))
); );
else else
append(tr("%1 points from %2's %3 to %4's %5.") myAppend(tr("%1 points from %2's %3 to %4's %5.")
.arg(sanitizeHtml(player->getName())) .arg(sanitizeHtml(player->getName()))
.arg(sanitizeHtml(startPlayer->getName())) .arg(sanitizeHtml(startPlayer->getName()))
.arg(sanitizeHtml(startCard)) .arg(sanitizeHtml(startCard))
@ -349,7 +361,7 @@ void MessageLogWidget::logSetCardCounter(Player *player, QString cardName, int c
default: ; default: ;
} }
append(finalStr.arg(sanitizeHtml(player->getName())).arg(colorStr).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName))).arg(value)); myAppend(finalStr.arg(sanitizeHtml(player->getName())).arg(colorStr).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(cardName))).arg(value));
} }
void MessageLogWidget::logSetTapped(Player *player, CardItem *card, bool tapped) void MessageLogWidget::logSetTapped(Player *player, CardItem *card, bool tapped)
@ -367,13 +379,13 @@ void MessageLogWidget::logSetTapped(Player *player, CardItem *card, bool tapped)
cardStr = isFemale(player) ? tr("her permanents") : tr("his permanents"); cardStr = isFemale(player) ? tr("her permanents") : tr("his permanents");
else else
cardStr = QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName())); cardStr = QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName()));
append(tr("%1 %2 %3.").arg(sanitizeHtml(player->getName())).arg(tapped ? tr("taps") : tr("untaps")).arg(cardStr)); myAppend(tr("%1 %2 %3.").arg(sanitizeHtml(player->getName())).arg(tapped ? tr("taps") : tr("untaps")).arg(cardStr));
} }
} }
void MessageLogWidget::logSetCounter(Player *player, QString counterName, int value, int oldValue) void MessageLogWidget::logSetCounter(Player *player, QString counterName, int value, int oldValue)
{ {
append(tr("%1 sets counter %2 to %3 (%4%5).").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(counterName))).arg(QString("<font color=\"blue\">%1</font>").arg(value)).arg(value > oldValue ? "+" : "").arg(value - oldValue)); myAppend(tr("%1 sets counter %2 to %3 (%4%5).").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(counterName))).arg(QString("<font color=\"blue\">%1</font>").arg(value)).arg(value > oldValue ? "+" : "").arg(value - oldValue));
} }
void MessageLogWidget::logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap) void MessageLogWidget::logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap)
@ -383,7 +395,7 @@ void MessageLogWidget::logSetDoesntUntap(Player *player, CardItem *card, bool do
finalStr = tr("%1 sets %2 to not untap normally."); finalStr = tr("%1 sets %2 to not untap normally.");
else else
finalStr = tr("%1 sets %2 to untap normally."); finalStr = tr("%1 sets %2 to untap normally.");
append(finalStr.arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName())))); myAppend(finalStr.arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName()))));
} }
void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT) void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT)
@ -391,26 +403,26 @@ void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT)
if (currentContext == MessageContext_MoveCard) if (currentContext == MessageContext_MoveCard)
moveCardPT.insert(card, newPT); moveCardPT.insert(card, newPT);
else else
append(tr("%1 sets PT of %2 to %3.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName()))).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(newPT)))); myAppend(tr("%1 sets PT of %2 to %3.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName()))).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(newPT))));
} }
void MessageLogWidget::logSetAnnotation(Player *player, CardItem *card, QString newAnnotation) void MessageLogWidget::logSetAnnotation(Player *player, CardItem *card, QString newAnnotation)
{ {
append(tr("%1 sets annotation of %2 to %3.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName()))).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(newAnnotation)))); myAppend(tr("%1 sets annotation of %2 to %3.").arg(sanitizeHtml(player->getName())).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(card->getName()))).arg(QString("<font color=\"blue\">%1</font>").arg(sanitizeHtml(newAnnotation))));
} }
void MessageLogWidget::logDumpZone(Player *player, CardZone *zone, int numberCards) void MessageLogWidget::logDumpZone(Player *player, CardZone *zone, int numberCards)
{ {
if (numberCards != -1) if (numberCards != -1)
append(tr("%1 is looking at the top %2 cards %3.").arg(sanitizeHtml(player->getName())).arg(numberCards).arg(zone->getTranslatedName(zone->getPlayer() == player, CaseGenitive))); myAppend(tr("%1 is looking at the top %2 cards %3.").arg(sanitizeHtml(player->getName())).arg(numberCards).arg(zone->getTranslatedName(zone->getPlayer() == player, CaseGenitive)));
else else
append(tr("%1 is looking at %2.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(zone->getPlayer() == player, CaseAccusative))); myAppend(tr("%1 is looking at %2.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(zone->getPlayer() == player, CaseAccusative)));
} }
void MessageLogWidget::logStopDumpZone(Player *player, CardZone *zone) void MessageLogWidget::logStopDumpZone(Player *player, CardZone *zone)
{ {
QString zoneName = zone->getTranslatedName(zone->getPlayer() == player, CaseAccusative); QString zoneName = zone->getTranslatedName(zone->getPlayer() == player, CaseAccusative);
append(tr("%1 stops looking at %2.").arg(sanitizeHtml(player->getName())).arg(zoneName)); myAppend(tr("%1 stops looking at %2.").arg(sanitizeHtml(player->getName())).arg(zoneName));
} }
void MessageLogWidget::logRevealCards(Player *player, CardZone *zone, int cardId, QString cardName, Player *otherPlayer) void MessageLogWidget::logRevealCards(Player *player, CardZone *zone, int cardId, QString cardName, Player *otherPlayer)
@ -433,28 +445,28 @@ void MessageLogWidget::logRevealCards(Player *player, CardZone *zone, int cardId
if (cardId == -1) { if (cardId == -1) {
if (otherPlayer) if (otherPlayer)
append(tr("%1 reveals %2 to %3.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(true, CaseAccusative)).arg(sanitizeHtml(otherPlayer->getName()))); myAppend(tr("%1 reveals %2 to %3.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(true, CaseAccusative)).arg(sanitizeHtml(otherPlayer->getName())));
else else
append(tr("%1 reveals %2.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(true, CaseAccusative))); myAppend(tr("%1 reveals %2.").arg(sanitizeHtml(player->getName())).arg(zone->getTranslatedName(true, CaseAccusative)));
} else if (cardId == -2) { } else if (cardId == -2) {
if (otherPlayer) if (otherPlayer)
append(tr("%1 randomly reveals %2%3 to %4.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr).arg(sanitizeHtml(otherPlayer->getName()))); myAppend(tr("%1 randomly reveals %2%3 to %4.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr).arg(sanitizeHtml(otherPlayer->getName())));
else else
append(tr("%1 randomly reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr)); myAppend(tr("%1 randomly reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr));
} else { } else {
if (otherPlayer) if (otherPlayer)
append(tr("%1 reveals %2%3 to %4.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr).arg(sanitizeHtml(otherPlayer->getName()))); myAppend(tr("%1 reveals %2%3 to %4.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr).arg(sanitizeHtml(otherPlayer->getName())));
else else
append(tr("%1 reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr)); myAppend(tr("%1 reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr));
} }
} }
void MessageLogWidget::logSetActivePlayer(Player *player) void MessageLogWidget::logSetActivePlayer(Player *player)
{ {
soundEngine->notification(); soundEngine->notification();
append(QString()); myAppend(QString());
append("<font color=\"green\"><b>" + tr("It is now %1's turn.").arg(player->getName()) + "</b></font>"); myAppend("<font color=\"green\"><b>" + tr("It is now %1's turn.").arg(player->getName()) + "</b></font>");
append(QString()); myAppend(QString());
} }
void MessageLogWidget::logSetActivePhase(int phase) void MessageLogWidget::logSetActivePhase(int phase)
@ -474,7 +486,7 @@ void MessageLogWidget::logSetActivePhase(int phase)
case 9: phaseName = tr("second main phase"); break; case 9: phaseName = tr("second main phase"); break;
case 10: phaseName = tr("ending phase"); break; case 10: phaseName = tr("ending phase"); break;
} }
append("<font color=\"green\"><b>" + tr("It is now the %1.").arg(phaseName) + "</b></font>"); myAppend("<font color=\"green\"><b>" + tr("It is now the %1.").arg(phaseName) + "</b></font>");
} }
void MessageLogWidget::containerProcessingStarted(GameEventContext *_context) void MessageLogWidget::containerProcessingStarted(GameEventContext *_context)
@ -531,67 +543,7 @@ void MessageLogWidget::connectToPlayer(Player *player)
connect(player, SIGNAL(logRevealCards(Player *, CardZone *, int, QString, Player *)), this, SLOT(logRevealCards(Player *, CardZone *, int, QString, Player *))); connect(player, SIGNAL(logRevealCards(Player *, CardZone *, int, QString, Player *)), this, SLOT(logRevealCards(Player *, CardZone *, int, QString, Player *)));
} }
MessageLogWidget::MessageLogWidget(QWidget *parent) MessageLogWidget::MessageLogWidget(const QString &_ownName, QWidget *parent)
: QTextEdit(parent) : ChatView(_ownName, false, parent)
{ {
setReadOnly(true);
}
void MessageLogWidget::enterEvent(QEvent * /*event*/)
{
setMouseTracking(true);
}
void MessageLogWidget::leaveEvent(QEvent * /*event*/)
{
setMouseTracking(false);
}
QString MessageLogWidget::getCardNameUnderMouse(const QPoint &pos) const
{
QTextCursor cursor(cursorForPosition(pos));
QTextBlock block(cursor.block());
QTextBlock::iterator it;
for (it = block.begin(); !(it.atEnd()); ++it) {
QTextFragment frag = it.fragment();
if (!frag.contains(cursor.position()))
continue;
if (frag.charFormat().foreground().color() == Qt::blue)
return frag.text();
break;
}
return QString();
}
void MessageLogWidget::mouseMoveEvent(QMouseEvent *event)
{
QString cardName = getCardNameUnderMouse(event->pos());
if (!cardName.isEmpty()) {
viewport()->setCursor(Qt::PointingHandCursor);
emit cardNameHovered(cardName);
} else
viewport()->setCursor(Qt::IBeamCursor);
QTextEdit::mouseMoveEvent(event);
}
void MessageLogWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::MidButton) {
QString cardName = getCardNameUnderMouse(event->pos());
if (!cardName.isEmpty())
emit showCardInfoPopup(event->globalPos(), cardName);
}
QTextEdit::mousePressEvent(event);
}
void MessageLogWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::MidButton)
emit deleteCardInfoPopup();
QTextEdit::mouseReleaseEvent(event);
} }

View file

@ -1,15 +1,13 @@
#ifndef MESSAGELOGWIDGET_H #ifndef MESSAGELOGWIDGET_H
#define MESSAGELOGWIDGET_H #define MESSAGELOGWIDGET_H
#include <QTextEdit> #include "chatview.h"
#include <QAbstractSocket> #include <QAbstractSocket>
#include "translation.h" #include "translation.h"
#include "protocol_datastructures.h" #include "protocol_datastructures.h"
class Player; class Player;
class CardZone; class CardZone;
class QMouseEvent;
class QEvent;
class CardInfoWidget; class CardInfoWidget;
class GameEventContext; class GameEventContext;
class CardItem; class CardItem;
@ -24,16 +22,15 @@ struct LogMoveCard {
int newX; int newX;
}; };
class MessageLogWidget : public QTextEdit { class MessageLogWidget : public ChatView {
Q_OBJECT Q_OBJECT
private: private:
enum MessageContext { MessageContext_None, MessageContext_MoveCard, MessageContext_Mulligan }; enum MessageContext { MessageContext_None, MessageContext_MoveCard, MessageContext_Mulligan };
CardInfoWidget *infoWidget;
QString sanitizeHtml(QString dirty) const; QString sanitizeHtml(QString dirty) const;
void myAppend(const QString &message);
bool isFemale(Player *player) const; bool isFemale(Player *player) const;
QPair<QString, QString> getFromStr(CardZone *zone, QString cardName, int position) const; QPair<QString, QString> getFromStr(CardZone *zone, QString cardName, int position) const;
QString getCardNameUnderMouse(const QPoint &pos) const;
MessageContext currentContext; MessageContext currentContext;
QList<LogMoveCard> moveCardQueue; QList<LogMoveCard> moveCardQueue;
@ -42,10 +39,6 @@ private:
Player *mulliganPlayer; Player *mulliganPlayer;
int mulliganNumber; int mulliganNumber;
signals:
void cardNameHovered(QString cardName);
void showCardInfoPopup(QPoint pos, QString cardName);
void deleteCardInfoPopup();
public slots: public slots:
void logConnecting(QString hostname); void logConnecting(QString hostname);
void logConnected(); void logConnected();
@ -96,13 +89,7 @@ public slots:
void containerProcessingDone(); void containerProcessingDone();
public: public:
void connectToPlayer(Player *player); void connectToPlayer(Player *player);
MessageLogWidget(QWidget *parent = 0); MessageLogWidget(const QString &_ownName, QWidget *parent = 0);
protected:
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
}; };
#endif #endif

30
cockatrice/src/tab.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "tab.h"
#include "cardinfowidget.h"
#include <QDesktopWidget>
#include <QApplication>
Tab::Tab(TabSupervisor *_tabSupervisor, QWidget *parent)
: QWidget(parent), tabMenu(0), tabSupervisor(_tabSupervisor), contentsChanged(false), infoPopup(0)
{
}
void Tab::showCardInfoPopup(const QPoint &pos, const QString &cardName)
{
infoPopup = new CardInfoWidget(CardInfoWidget::ModePopUp, 0, Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint);
infoPopup->setAttribute(Qt::WA_TransparentForMouseEvents);
infoPopup->setCard(cardName);
QRect screenRect = qApp->desktop()->screenGeometry(this);
infoPopup->move(
qMax(screenRect.left(), qMin(pos.x() - infoPopup->width() / 2, screenRect.left() + screenRect.width() - infoPopup->width())),
qMax(screenRect.top(), qMin(pos.y() - infoPopup->height() / 2, screenRect.top() + screenRect.height() - infoPopup->height()))
);
infoPopup->show();
}
void Tab::deleteCardInfoPopup()
{
if (infoPopup) {
infoPopup->deleteLater();
infoPopup = 0;
}
}

View file

@ -5,6 +5,7 @@
class QMenu; class QMenu;
class TabSupervisor; class TabSupervisor;
class CardInfoWidget;
class Tab : public QWidget { class Tab : public QWidget {
Q_OBJECT Q_OBJECT
@ -13,11 +14,14 @@ signals:
protected: protected:
QMenu *tabMenu; QMenu *tabMenu;
TabSupervisor *tabSupervisor; TabSupervisor *tabSupervisor;
protected slots:
void showCardInfoPopup(const QPoint &pos, const QString &cardName);
void deleteCardInfoPopup();
private: private:
bool contentsChanged; bool contentsChanged;
CardInfoWidget *infoPopup;
public: public:
Tab(TabSupervisor *_tabSupervisor, QWidget *parent = 0) Tab(TabSupervisor *_tabSupervisor, QWidget *parent = 0);
: QWidget(parent), tabMenu(0), tabSupervisor(_tabSupervisor), contentsChanged(false) { }
QMenu *getTabMenu() const { return tabMenu; } QMenu *getTabMenu() const { return tabMenu; }
bool getContentsChanged() const { return contentsChanged; } bool getContentsChanged() const { return contentsChanged; }
void setContentsChanged(bool _contentsChanged) { contentsChanged = _contentsChanged; } void setContentsChanged(bool _contentsChanged) { contentsChanged = _contentsChanged; }

View file

@ -5,8 +5,6 @@
#include <QAction> #include <QAction>
#include <QMessageBox> #include <QMessageBox>
#include <QFileDialog> #include <QFileDialog>
#include <QApplication>
#include <QDesktopWidget>
#include "tab_game.h" #include "tab_game.h"
#include "cardinfowidget.h" #include "cardinfowidget.h"
#include "playerlistwidget.h" #include "playerlistwidget.h"
@ -160,8 +158,8 @@ void DeckViewContainer::setDeck(DeckList *deck)
readyStartButton->setEnabled(true); readyStartButton->setEnabled(true);
} }
TabGame::TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, int _gameId, const QString &_gameDescription, int _localPlayerId, bool _spectator, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, bool _resuming) TabGame::TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, int _gameId, const QString &_gameDescription, int _localPlayerId, const QString &_userName, bool _spectator, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, bool _resuming)
: Tab(_tabSupervisor), clients(_clients), gameId(_gameId), gameDescription(_gameDescription), localPlayerId(_localPlayerId), spectator(_spectator), spectatorsCanTalk(_spectatorsCanTalk), spectatorsSeeEverything(_spectatorsSeeEverything), started(false), resuming(_resuming), currentPhase(-1), infoPopup(0) : Tab(_tabSupervisor), clients(_clients), gameId(_gameId), gameDescription(_gameDescription), localPlayerId(_localPlayerId), spectator(_spectator), spectatorsCanTalk(_spectatorsCanTalk), spectatorsSeeEverything(_spectatorsSeeEverything), started(false), resuming(_resuming), currentPhase(-1)
{ {
phasesToolbar = new PhasesToolbar; phasesToolbar = new PhasesToolbar;
phasesToolbar->hide(); phasesToolbar->hide();
@ -178,7 +176,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_client
timeElapsedLabel = new QLabel; timeElapsedLabel = new QLabel;
timeElapsedLabel->setAlignment(Qt::AlignCenter); timeElapsedLabel->setAlignment(Qt::AlignCenter);
messageLog = new MessageLogWidget; messageLog = new MessageLogWidget(_userName);
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString))); connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString)));
connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(messageLog, SIGNAL(deleteCardInfoPopup()), this, SLOT(deleteCardInfoPopup())); connect(messageLog, SIGNAL(deleteCardInfoPopup()), this, SLOT(deleteCardInfoPopup()));
@ -779,24 +777,3 @@ Player *TabGame::getActiveLocalPlayer() const
return 0; return 0;
} }
void TabGame::showCardInfoPopup(const QPoint &pos, const QString &cardName)
{
infoPopup = new CardInfoWidget(CardInfoWidget::ModePopUp, 0, Qt::Widget | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint);
infoPopup->setAttribute(Qt::WA_TransparentForMouseEvents);
infoPopup->setCard(cardName);
QRect screenRect = qApp->desktop()->screenGeometry(this);
infoPopup->move(
qMax(screenRect.left(), qMin(pos.x() - infoPopup->width() / 2, screenRect.left() + screenRect.width() - infoPopup->width())),
qMax(screenRect.top(), qMin(pos.y() - infoPopup->height() / 2, screenRect.top() + screenRect.height() - infoPopup->height()))
);
infoPopup->show();
}
void TabGame::deleteCardInfoPopup()
{
if (infoPopup) {
infoPopup->deleteLater();
infoPopup = 0;
}
}

View file

@ -100,7 +100,6 @@ private:
int activePlayer; int activePlayer;
QSplitter *splitter; QSplitter *splitter;
CardInfoWidget *infoPopup;
CardInfoWidget *cardInfo; CardInfoWidget *cardInfo;
PlayerListWidget *playerListWidget; PlayerListWidget *playerListWidget;
QLabel *timeElapsedLabel; QLabel *timeElapsedLabel;
@ -147,8 +146,6 @@ signals:
void openMessageDialog(const QString &userName, bool focus); void openMessageDialog(const QString &userName, bool focus);
private slots: private slots:
void newCardAdded(AbstractCardItem *card); void newCardAdded(AbstractCardItem *card);
void showCardInfoPopup(const QPoint &pos, const QString &cardName);
void deleteCardInfoPopup();
void actConcede(); void actConcede();
void actLeaveGame(); void actLeaveGame();
@ -158,7 +155,7 @@ private slots:
void actNextPhase(); void actNextPhase();
void actNextTurn(); void actNextTurn();
public: public:
TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, int _gameId, const QString &_gameDescription, int _localPlayerId, bool _spectator, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, bool _resuming); TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, int _gameId, const QString &_gameDescription, int _localPlayerId, const QString &_userName, bool _spectator, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, bool _resuming);
~TabGame(); ~TabGame();
void retranslateUi(); void retranslateUi();
void closeRequest(); void closeRequest();

View file

@ -11,7 +11,9 @@
TabMessage::TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, const QString &_ownName, const QString &_userName) TabMessage::TabMessage(TabSupervisor *_tabSupervisor, AbstractClient *_client, const QString &_ownName, const QString &_userName)
: Tab(_tabSupervisor), client(_client), userName(_userName), userOnline(true) : Tab(_tabSupervisor), client(_client), userName(_userName), userOnline(true)
{ {
chatView = new ChatView(_ownName); chatView = new ChatView(_ownName, true);
connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(chatView, SIGNAL(deleteCardInfoPopup()), this, SLOT(deleteCardInfoPopup()));
sayEdit = new QLineEdit; sayEdit = new QLineEdit;
connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage())); connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage()));

View file

@ -137,7 +137,9 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, const Q
userList = new UserList(tabSupervisor, client, UserList::RoomList); userList = new UserList(tabSupervisor, client, UserList::RoomList);
connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool))); connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool)));
chatView = new ChatView(ownName); chatView = new ChatView(ownName, true);
connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(chatView, SIGNAL(deleteCardInfoPopup()), this, SLOT(deleteCardInfoPopup()));
sayLabel = new QLabel; sayLabel = new QLabel;
sayEdit = new QLineEdit; sayEdit = new QLineEdit;
sayLabel->setBuddy(sayEdit); sayLabel->setBuddy(sayEdit);

View file

@ -221,7 +221,7 @@ void TabSupervisor::addCloseButtonToTab(Tab *tab, int tabIndex)
void TabSupervisor::gameJoined(Event_GameJoined *event) void TabSupervisor::gameJoined(Event_GameJoined *event)
{ {
TabGame *tab = new TabGame(this, QList<AbstractClient *>() << client, event->getGameId(), event->getGameDescription(), event->getPlayerId(), event->getSpectator(), event->getSpectatorsCanTalk(), event->getSpectatorsSeeEverything(), event->getResuming()); TabGame *tab = new TabGame(this, QList<AbstractClient *>() << client, event->getGameId(), event->getGameDescription(), event->getPlayerId(), userName, event->getSpectator(), event->getSpectatorsCanTalk(), event->getSpectatorsSeeEverything(), event->getResuming());
connect(tab, SIGNAL(gameClosing(TabGame *)), this, SLOT(gameLeft(TabGame *))); connect(tab, SIGNAL(gameClosing(TabGame *)), this, SLOT(gameLeft(TabGame *)));
connect(tab, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool))); connect(tab, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool)));
int tabIndex = myAddTab(tab); int tabIndex = myAddTab(tab);
@ -232,7 +232,7 @@ void TabSupervisor::gameJoined(Event_GameJoined *event)
void TabSupervisor::localGameJoined(Event_GameJoined *event) void TabSupervisor::localGameJoined(Event_GameJoined *event)
{ {
TabGame *tab = new TabGame(this, localClients, event->getGameId(), event->getGameDescription(), event->getPlayerId(), event->getSpectator(), event->getSpectatorsCanTalk(), event->getSpectatorsSeeEverything(), event->getResuming()); TabGame *tab = new TabGame(this, localClients, event->getGameId(), event->getGameDescription(), event->getPlayerId(), QString(), event->getSpectator(), event->getSpectatorsCanTalk(), event->getSpectatorsSeeEverything(), event->getResuming());
connect(tab, SIGNAL(gameClosing(TabGame *)), this, SLOT(gameLeft(TabGame *))); connect(tab, SIGNAL(gameClosing(TabGame *)), this, SLOT(gameLeft(TabGame *)));
int tabIndex = myAddTab(tab); int tabIndex = myAddTab(tab);
addCloseButtonToTab(tab, tabIndex); addCloseButtonToTab(tab, tabIndex);

View file

@ -10,6 +10,53 @@
#include <QMouseEvent> #include <QMouseEvent>
#include <QMenu> #include <QMenu>
#include <QInputDialog> #include <QInputDialog>
#include <QLabel>
#include <QSpinBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QHBoxLayout>
BanDialog::BanDialog(QWidget *parent)
: QDialog(parent)
{
QLabel *durationLabel = new QLabel(tr("Please enter the duration of the ban (in minutes).\nEnter 0 for an indefinite ban."));
durationEdit = new QSpinBox;
durationEdit->setMinimum(0);
durationEdit->setValue(5);
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;
QPushButton *okButton = new QPushButton(tr("&OK"));
okButton->setAutoDefault(true);
connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
QPushButton *cancelButton = new QPushButton(tr("&Cancel"));
connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
buttonLayout->addWidget(okButton);
buttonLayout->addWidget(cancelButton);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(durationLabel);
vbox->addWidget(durationEdit);
vbox->addWidget(reasonLabel);
vbox->addWidget(reasonEdit);
vbox->addLayout(buttonLayout);
setLayout(vbox);
setWindowTitle(tr("Ban user from server"));
}
int BanDialog::getMinutes() const
{
return durationEdit->value();
}
QString BanDialog::getReason() const
{
return reasonEdit->toPlainText();
}
UserListItemDelegate::UserListItemDelegate(QObject *const parent) UserListItemDelegate::UserListItemDelegate(QObject *const parent)
: QStyledItemDelegate(parent) : QStyledItemDelegate(parent)
@ -215,10 +262,9 @@ void UserList::showContextMenu(const QPoint &pos, const QModelIndex &index)
else if (actionClicked == aRemoveFromIgnoreList) else if (actionClicked == aRemoveFromIgnoreList)
client->sendCommand(new Command_RemoveFromList("ignore", userName)); client->sendCommand(new Command_RemoveFromList("ignore", userName));
else if (actionClicked == aBan) { else if (actionClicked == aBan) {
bool ok; BanDialog dlg(this);
int minutes = QInputDialog::getInt(this, tr("Duration"), tr("Please enter the duration of the ban (in minutes).\nEnter 0 for an indefinite ban."), 0, 0, 2147483647, 10, &ok); if (dlg.exec())
if (ok) client->sendCommand(new Command_BanFromServer(userName, dlg.getMinutes(), dlg.getReason()));
client->sendCommand(new Command_BanFromServer(userName, minutes));
} }
delete menu; delete menu;

View file

@ -1,6 +1,7 @@
#ifndef USERLIST_H #ifndef USERLIST_H
#define USERLIST_H #define USERLIST_H
#include <QDialog>
#include <QGroupBox> #include <QGroupBox>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
@ -9,6 +10,19 @@ class QTreeWidget;
class ServerInfo_User; class ServerInfo_User;
class AbstractClient; class AbstractClient;
class TabSupervisor; class TabSupervisor;
class QSpinBox;
class QPlainTextEdit;
class BanDialog : public QDialog {
Q_OBJECT
private:
QSpinBox *durationEdit;
QPlainTextEdit *reasonEdit;
public:
BanDialog(QWidget *parent = 0);
int getMinutes() const;
QString getReason() const;
};
class UserListItemDelegate : public QStyledItemDelegate { class UserListItemDelegate : public QStyledItemDelegate {
public: public:

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -484,11 +484,12 @@ Command_ShutdownServer::Command_ShutdownServer(const QString &_reason, int _minu
insertItem(new SerializableItem_String("reason", _reason)); insertItem(new SerializableItem_String("reason", _reason));
insertItem(new SerializableItem_Int("minutes", _minutes)); insertItem(new SerializableItem_Int("minutes", _minutes));
} }
Command_BanFromServer::Command_BanFromServer(const QString &_userName, int _minutes) Command_BanFromServer::Command_BanFromServer(const QString &_userName, int _minutes, const QString &_reason)
: ModeratorCommand("ban_from_server") : ModeratorCommand("ban_from_server")
{ {
insertItem(new SerializableItem_String("user_name", _userName)); insertItem(new SerializableItem_String("user_name", _userName));
insertItem(new SerializableItem_Int("minutes", _minutes)); insertItem(new SerializableItem_Int("minutes", _minutes));
insertItem(new SerializableItem_String("reason", _reason));
} }
void ProtocolItem::initializeHashAuto() void ProtocolItem::initializeHashAuto()
{ {

View file

@ -80,4 +80,4 @@
6:mulligan:i,number 6:mulligan:i,number
7:update_server_message 7:update_server_message
7:shutdown_server:s,reason:i,minutes 7:shutdown_server:s,reason:i,minutes
8:ban_from_server:s,user_name:i,minutes 8:ban_from_server:s,user_name:i,minutes:s,reason

View file

@ -735,9 +735,10 @@ public:
class Command_BanFromServer : public ModeratorCommand { class Command_BanFromServer : public ModeratorCommand {
Q_OBJECT Q_OBJECT
public: public:
Command_BanFromServer(const QString &_userName = QString(), int _minutes = -1); Command_BanFromServer(const QString &_userName = QString(), int _minutes = -1, const QString &_reason = QString());
QString getUserName() const { return static_cast<SerializableItem_String *>(itemMap.value("user_name"))->getData(); }; QString getUserName() const { return static_cast<SerializableItem_String *>(itemMap.value("user_name"))->getData(); };
int getMinutes() const { return static_cast<SerializableItem_Int *>(itemMap.value("minutes"))->getData(); }; int getMinutes() const { return static_cast<SerializableItem_Int *>(itemMap.value("minutes"))->getData(); };
QString getReason() const { return static_cast<SerializableItem_String *>(itemMap.value("reason"))->getData(); };
static SerializableItem *newItem() { return new Command_BanFromServer; } static SerializableItem *newItem() { return new Command_BanFromServer; }
int getItemId() const { return ItemId_Command_BanFromServer; } int getItemId() const { return ItemId_Command_BanFromServer; }
}; };

View file

@ -52,7 +52,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
QMutexLocker locker(&serverMutex); QMutexLocker locker(&serverMutex);
if (name.size() > 35) if (name.size() > 35)
name = name.left(35); name = name.left(35);
AuthenticationResult authState = checkUserPassword(name, password); AuthenticationResult authState = checkUserPassword(session, name, password);
if (authState == PasswordWrong) if (authState == PasswordWrong)
return authState; return authState;

View file

@ -43,7 +43,6 @@ public:
virtual QMap<QString, ServerInfo_User *> getBuddyList(const QString &name) = 0; virtual QMap<QString, ServerInfo_User *> getBuddyList(const QString &name) = 0;
virtual QMap<QString, ServerInfo_User *> getIgnoreList(const QString &name) = 0; virtual QMap<QString, ServerInfo_User *> getIgnoreList(const QString &name) = 0;
virtual bool getUserBanned(Server_ProtocolHandler * /*client*/, const QString & /*userName*/) const { return false; }
protected: protected:
void prepareDestroy(); void prepareDestroy();
QList<Server_ProtocolHandler *> clients; QList<Server_ProtocolHandler *> clients;
@ -51,7 +50,7 @@ protected:
QMap<int, Server_Room *> rooms; QMap<int, Server_Room *> rooms;
virtual bool userExists(const QString &user) = 0; virtual bool userExists(const QString &user) = 0;
virtual AuthenticationResult checkUserPassword(const QString &user, const QString &password) = 0; virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password) = 0;
virtual ServerInfo_User *getUserData(const QString &name) = 0; virtual ServerInfo_User *getUserData(const QString &name) = 0;
int getUsersCount() const; int getUsersCount() const;
int getGamesCount() const; int getGamesCount() const;

View file

@ -280,8 +280,6 @@ ResponseCode Server_ProtocolHandler::cmdLogin(Command_Login *cmd, CommandContain
QString userName = cmd->getUsername().simplified(); QString userName = cmd->getUsername().simplified();
if (userName.isEmpty() || (userInfo != 0)) if (userName.isEmpty() || (userInfo != 0))
return RespContextError; return RespContextError;
if (server->getUserBanned(this, userName))
return RespWrongPassword;
authState = server->loginUser(this, userName, cmd->getPassword()); authState = server->loginUser(this, userName, cmd->getPassword());
if (authState == PasswordWrong) if (authState == PasswordWrong)
return RespWrongPassword; return RespWrongPassword;

View file

@ -111,7 +111,6 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` (
`avatar_bmp` blob NOT NULL, `avatar_bmp` blob NOT NULL,
`registrationDate` datetime NOT NULL, `registrationDate` datetime NOT NULL,
`active` tinyint(1) NOT NULL, `active` tinyint(1) NOT NULL,
`banned` tinyint(1) NOT NULL,
`token` char(32) NOT NULL, `token` char(32) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) UNIQUE KEY `name` (`name`)
@ -149,3 +148,12 @@ CREATE TABLE `cockatrice_buddylist` (
KEY `id_user2` (`id_user2`) KEY `id_user2` (`id_user2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `cockatrice_bans` (
`id_user` int(7) unsigned zerofill NOT NULL,
`id_admin` int(7) unsigned zerofill NOT NULL,
`time_from` datetime NOT NULL,
`minutes` int(6) NOT NULL,
`reason` text NOT NULL,
KEY `id_user` (`id_user`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

View file

@ -169,25 +169,32 @@ bool Servatrice::execSqlQuery(QSqlQuery &query)
return false; return false;
} }
AuthenticationResult Servatrice::checkUserPassword(const QString &user, const QString &password) AuthenticationResult Servatrice::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password)
{ {
serverMutex.lock();
QHostAddress address = static_cast<ServerSocketInterface *>(handler)->getPeerAddress();
for (int i = 0; i < addressBanList.size(); ++i)
if (address == addressBanList[i].first)
return PasswordWrong;
serverMutex.unlock();
QMutexLocker locker(&dbMutex); QMutexLocker locker(&dbMutex);
const QString method = settings->value("authentication/method").toString(); const QString method = settings->value("authentication/method").toString();
if (method == "none") if (method == "none")
return UnknownUser; return UnknownUser;
else if (method == "sql") { else if (method == "sql") {
checkSql(); checkSql();
QSqlQuery query; QSqlQuery query;
query.prepare("select banned, password from " + dbPrefix + "_users where name = :name and active = 1"); query.prepare("select a.password, timediff(now(), date_add(b.time_from, interval b.minutes minute)) < 0, b.minutes <=> 0 from " + dbPrefix + "_users a left join " + dbPrefix + "_bans b on b.id_user = a.id and b.time_from = (select max(c.time_from) from " + dbPrefix + "_bans c where c.id_user = a.id) where a.name = :name and a.active = 1");
query.bindValue(":name", user); query.bindValue(":name", user);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
return PasswordWrong; return PasswordWrong;
if (query.next()) { if (query.next()) {
if (query.value(0).toInt()) if (query.value(1).toInt() || query.value(2).toInt())
return PasswordWrong; return PasswordWrong;
if (query.value(1).toString() == password) if (query.value(0).toString() == password)
return PasswordRight; return PasswordRight;
else else
return PasswordWrong; return PasswordWrong;
@ -325,19 +332,6 @@ QMap<QString, ServerInfo_User *> Servatrice::getIgnoreList(const QString &name)
return result; return result;
} }
bool Servatrice::getUserBanned(Server_ProtocolHandler *client, const QString &userName) const
{
QMutexLocker locker(&serverMutex);
QHostAddress address = static_cast<ServerSocketInterface *>(client)->getPeerAddress();
for (int i = 0; i < addressBanList.size(); ++i)
if (address == addressBanList[i].first)
return true;
for (int i = 0; i < nameBanList.size(); ++i)
if (userName == nameBanList[i].first)
return true;
return false;
}
void Servatrice::updateBanTimer() void Servatrice::updateBanTimer()
{ {
QMutexLocker locker(&serverMutex); QMutexLocker locker(&serverMutex);
@ -346,11 +340,6 @@ void Servatrice::updateBanTimer()
addressBanList.removeAt(i); addressBanList.removeAt(i);
else else
++i; ++i;
for (int i = 0; i < nameBanList.size(); )
if (--(nameBanList[i].second) <= 0)
nameBanList.removeAt(i);
else
++i;
} }
void Servatrice::updateLoginMessage() void Servatrice::updateLoginMessage()

View file

@ -74,13 +74,11 @@ public:
int getUsersWithAddress(const QHostAddress &address) const; int getUsersWithAddress(const QHostAddress &address) const;
QMap<QString, ServerInfo_User *> getBuddyList(const QString &name); QMap<QString, ServerInfo_User *> getBuddyList(const QString &name);
QMap<QString, ServerInfo_User *> getIgnoreList(const QString &name); QMap<QString, ServerInfo_User *> getIgnoreList(const QString &name);
bool getUserBanned(Server_ProtocolHandler *client, const QString &userName) const;
void addAddressBan(const QHostAddress &address, int minutes) { addressBanList.append(QPair<QHostAddress, int>(address, minutes)); } void addAddressBan(const QHostAddress &address, int minutes) { addressBanList.append(QPair<QHostAddress, int>(address, minutes)); }
void addNameBan(const QString &name, int minutes) { nameBanList.append(QPair<QString, int>(name, minutes)); }
void scheduleShutdown(const QString &reason, int minutes); void scheduleShutdown(const QString &reason, int minutes);
protected: protected:
bool userExists(const QString &user); bool userExists(const QString &user);
AuthenticationResult checkUserPassword(const QString &user, const QString &password); AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password);
private: private:
QTimer *pingClock, *statusUpdateClock, *banTimeoutClock; QTimer *pingClock, *statusUpdateClock, *banTimeoutClock;
QTcpServer *tcpServer; QTcpServer *tcpServer;
@ -90,7 +88,6 @@ private:
int serverId; int serverId;
int uptime; int uptime;
QList<QPair<QHostAddress, int> > addressBanList; QList<QPair<QHostAddress, int> > addressBanList;
QList<QPair<QString, int> > nameBanList;
int maxGameInactivityTime, maxPlayerInactivityTime; int maxGameInactivityTime, maxPlayerInactivityTime;
int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser; int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser;
ServerInfo_User *evalUserQueryResult(const QSqlQuery &query, bool complete); ServerInfo_User *evalUserQueryResult(const QSqlQuery &query, bool complete);

View file

@ -488,14 +488,14 @@ ResponseCode ServerSocketInterface::cmdBanFromServer(Command_BanFromServer *cmd,
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName)); ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
if (user->getUserInfo()->getUserLevel() & ServerInfo_User::IsRegistered) { if (user->getUserInfo()->getUserLevel() & ServerInfo_User::IsRegistered) {
// Registered users can be banned by name. // Registered users can be banned by name.
if (minutes == 0) { QMutexLocker locker(&servatrice->dbMutex);
QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query;
QSqlQuery query; query.prepare("insert into " + servatrice->getDbPrefix() + "_bans (id_user, id_admin, time_from, minutes, reason) values(:id_user, :id_admin, NOW(), :minutes, :reason)");
query.prepare("update " + servatrice->getDbPrefix() + "_users set banned=1 where name = :name"); query.bindValue(":id_user", getUserIdInDB(userName));
query.bindValue(":name", userName); query.bindValue(":id_admin", getUserIdInDB(userInfo->getName()));
servatrice->execSqlQuery(query); query.bindValue(":minutes", minutes);
} else query.bindValue(":reason", cmd->getReason());
servatrice->addNameBan(userName, minutes); servatrice->execSqlQuery(query);
} else { } else {
// Unregistered users must be banned by IP address. // Unregistered users must be banned by IP address.
// Indefinite address bans are not reasonable -> default to 30 minutes. // Indefinite address bans are not reasonable -> default to 30 minutes.