diff --git a/cockatrice/cockatrice.pro b/cockatrice/cockatrice.pro index afb8344d..d9893b1d 100644 --- a/cockatrice/cockatrice.pro +++ b/cockatrice/cockatrice.pro @@ -58,6 +58,7 @@ HEADERS += src/abstractcounter.h \ src/tab_deck_storage.h \ src/tab_supervisor.h \ src/tab_admin.h \ + src/chatview.h \ src/userlist.h \ src/userinfobox.h \ src/remotedecklist_treewidget.h \ @@ -139,6 +140,7 @@ SOURCES += src/abstractcounter.cpp \ src/tab_deck_storage.cpp \ src/tab_supervisor.cpp \ src/tab_admin.cpp \ + src/chatview.cpp \ src/userlist.cpp \ src/userinfobox.cpp \ src/remotedecklist_treewidget.cpp \ diff --git a/cockatrice/src/chatview.cpp b/cockatrice/src/chatview.cpp new file mode 100644 index 00000000..a23b5a6e --- /dev/null +++ b/cockatrice/src/chatview.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include "chatview.h" + +ChatView::ChatView(const QString &_ownName, QWidget *parent) + : QTextEdit(parent), ownName(_ownName) +{ + setTextInteractionFlags(Qt::TextSelectableByMouse); + + QTextTableFormat format; + format.setBorderStyle(QTextFrameFormat::BorderStyle_None); + table = textCursor().insertTable(1, 3, format); +} + +void ChatView::appendMessage(const QString &sender, const QString &message) +{ + QTextCursor cellCursor = table->cellAt(table->rows() - 1, 0).lastCursorPosition(); + cellCursor.insertText(QDateTime::currentDateTime().toString("[hh:mm]")); + QTextTableCell senderCell = table->cellAt(table->rows() - 1, 1); + QTextCharFormat senderFormat; + if (sender == ownName) { + senderFormat.setFontWeight(QFont::Bold); + senderFormat.setForeground(Qt::red); + } else + senderFormat.setForeground(Qt::blue); + senderCell.setFormat(senderFormat); + cellCursor = senderCell.lastCursorPosition(); + cellCursor.insertText(sender); + QTextTableCell messageCell = table->cellAt(table->rows() - 1, 2); + QTextCharFormat messageFormat; + if (sender.isEmpty()) + messageFormat.setForeground(Qt::darkGreen); + messageCell.setFormat(messageFormat); + cellCursor = messageCell.lastCursorPosition(); + cellCursor.insertText(message); + + table->appendRows(1); + + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} \ No newline at end of file diff --git a/cockatrice/src/chatview.h b/cockatrice/src/chatview.h new file mode 100644 index 00000000..fc9a2626 --- /dev/null +++ b/cockatrice/src/chatview.h @@ -0,0 +1,18 @@ +#ifndef CHATVIEW_H +#define CHATVIEW_H + +#include + +class QTextTable; + +class ChatView : public QTextEdit { + Q_OBJECT; +private: + QTextTable *table; + QString ownName; +public: + ChatView(const QString &_ownName, QWidget *parent = 0); + void appendMessage(const QString &sender, const QString &message); +}; + +#endif \ No newline at end of file diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 3d80a340..359cea4b 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -886,6 +886,11 @@ void Player::eventDestroyCard(Event_DestroyCard *event) if (!card) return; + QList attachedCards = card->getAttachedCards(); + // This list is always empty except for buggy server implementations. + for (int i = 0; i < attachedCards.size(); ++i) + attachedCards[i]->setAttachedTo(0); + emit logDestroyCard(this, card->getName()); zone->takeCard(-1, event->getCardId(), true); card->deleteLater(); diff --git a/cockatrice/src/tab_message.cpp b/cockatrice/src/tab_message.cpp index 45446f41..339a293f 100644 --- a/cockatrice/src/tab_message.cpp +++ b/cockatrice/src/tab_message.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,17 +6,17 @@ #include "tab_message.h" #include "abstractclient.h" #include "protocol_items.h" +#include "chatview.h" -TabMessage::TabMessage(AbstractClient *_client, const QString &_userName) - : Tab(), client(_client), userName(_userName) +TabMessage::TabMessage(AbstractClient *_client, const QString &_ownName, const QString &_userName) + : Tab(), client(_client), userName(_userName), userOnline(true) { - textEdit = new QTextEdit; - textEdit->setReadOnly(true); + chatView = new ChatView(_ownName); sayEdit = new QLineEdit; connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage())); QVBoxLayout *vbox = new QVBoxLayout; - vbox->addWidget(textEdit); + vbox->addWidget(chatView); vbox->addWidget(sayEdit); aLeave = new QAction(this); @@ -41,17 +40,9 @@ void TabMessage::retranslateUi() aLeave->setText(tr("&Leave")); } -QString TabMessage::sanitizeHtml(QString dirty) const -{ - return dirty - .replace("&", "&") - .replace("<", "<") - .replace(">", ">"); -} - void TabMessage::sendMessage() { - if (sayEdit->text().isEmpty()) + if (sayEdit->text().isEmpty() || !userOnline) return; client->sendCommand(new Command_Message(userName, sayEdit->text())); @@ -65,14 +56,18 @@ void TabMessage::actLeave() void TabMessage::processMessageEvent(Event_Message *event) { - textEdit->append(QString("getSenderName() == userName ? "#0000fe" : "red") + QString("\">%1: %2").arg(sanitizeHtml(event->getSenderName())).arg(sanitizeHtml(event->getText()))); + chatView->appendMessage(event->getSenderName(), event->getText()); emit userEvent(); } -void TabMessage::processUserLeft(const QString &name) +void TabMessage::processUserLeft() { - if (userName == name) { - textEdit->append("" + tr("%1 has left the server.").arg(sanitizeHtml(name)) + ""); - sayEdit->setEnabled(false); - } + chatView->appendMessage(QString(), tr("%1 has left the server.").arg(userName)); + userOnline = false; +} + +void TabMessage::processUserJoined() +{ + chatView->appendMessage(QString(), tr("%1 has joined the server.").arg(userName)); + userOnline = true; } diff --git a/cockatrice/src/tab_message.h b/cockatrice/src/tab_message.h index 0bb240f3..868f6316 100644 --- a/cockatrice/src/tab_message.h +++ b/cockatrice/src/tab_message.h @@ -4,7 +4,7 @@ #include "tab.h" class AbstractClient; -class QTextEdit; +class ChatView; class QLineEdit; class Event_Message; @@ -13,26 +13,27 @@ class TabMessage : public Tab { private: AbstractClient *client; QString userName; + bool userOnline; - QTextEdit *textEdit; + ChatView *chatView; QLineEdit *sayEdit; QAction *aLeave; - QString sanitizeHtml(QString dirty) const; signals: void talkClosing(TabMessage *tab); private slots: void sendMessage(); void actLeave(); -public slots: - void processMessageEvent(Event_Message *event); - void processUserLeft(const QString &userName); public: - TabMessage(AbstractClient *_client, const QString &_userName); + TabMessage(AbstractClient *_client, const QString &_ownName, const QString &_userName); ~TabMessage(); void retranslateUi(); QString getUserName() const { return userName; } QString getTabText() const { return tr("Talking to %1").arg(userName); } + + void processMessageEvent(Event_Message *event); + void processUserLeft(); + void processUserJoined(); }; #endif diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index a8258fd7..9493cee0 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -10,15 +9,13 @@ #include #include #include -#include #include "dlg_creategame.h" #include "tab_room.h" #include "userlist.h" #include "abstractclient.h" #include "protocol_items.h" #include "gamesmodel.h" - -#include +#include "chatview.h" GameSelector::GameSelector(AbstractClient *_client, int _roomId, QWidget *parent) : QGroupBox(parent), client(_client), roomId(_roomId) @@ -122,43 +119,6 @@ void GameSelector::processGameInfo(ServerInfo_Game *info) gameListModel->updateGameList(info); } -ChatView::ChatView(const QString &_ownName, QWidget *parent) - : QTextEdit(parent), ownName(_ownName) -{ - setTextInteractionFlags(Qt::TextSelectableByMouse); - - QTextTableFormat format; - format.setBorderStyle(QTextFrameFormat::BorderStyle_None); - table = textCursor().insertTable(1, 3, format); -} - -void ChatView::appendMessage(const QString &sender, const QString &message) -{ - QTextCursor cellCursor = table->cellAt(table->rows() - 1, 0).lastCursorPosition(); - cellCursor.insertText(QDateTime::currentDateTime().toString("[hh:mm]")); - QTextTableCell senderCell = table->cellAt(table->rows() - 1, 1); - QTextCharFormat senderFormat; - if (sender == ownName) { - senderFormat.setFontWeight(QFont::Bold); - senderFormat.setForeground(Qt::red); - } else - senderFormat.setForeground(Qt::blue); - senderCell.setFormat(senderFormat); - cellCursor = senderCell.lastCursorPosition(); - cellCursor.insertText(sender); - QTextTableCell messageCell = table->cellAt(table->rows() - 1, 2); - QTextCharFormat messageFormat; - if (sender.isEmpty()) - messageFormat.setForeground(Qt::darkGreen); - messageCell.setFormat(messageFormat); - cellCursor = messageCell.lastCursorPosition(); - cellCursor.insertText(message); - - table->appendRows(1); - - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); -} - TabRoom::TabRoom(AbstractClient *_client, const QString &_ownName, ServerInfo_Room *info) : Tab(), client(_client), roomId(info->getRoomId()), roomName(info->getName()), ownName(_ownName) { diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index fd4f8f5c..d9ac35af 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -4,12 +4,11 @@ #include "tab.h" #include "protocol_datastructures.h" #include -#include class AbstractClient; class UserList; class QLabel; -class QTextEdit; +class ChatView; class QLineEdit; class QTreeView; class QPushButton; @@ -49,16 +48,6 @@ public: void processGameInfo(ServerInfo_Game *info); }; -class ChatView : public QTextEdit { - Q_OBJECT; -private: - QTextTable *table; - QString ownName; -public: - ChatView(const QString &_ownName, QWidget *parent = 0); - void appendMessage(const QString &sender, const QString &message); -}; - class TabRoom : public Tab { Q_OBJECT private: diff --git a/cockatrice/src/tab_server.cpp b/cockatrice/src/tab_server.cpp index f2ab909a..f92bf581 100644 --- a/cockatrice/src/tab_server.cpp +++ b/cockatrice/src/tab_server.cpp @@ -184,6 +184,8 @@ void TabServer::processUserJoinedEvent(Event_UserJoined *event) { userList->processUserInfo(event->getUserInfo()); userList->sortItems(); + + emit userJoined(event->getUserInfo()->getName()); } void TabServer::processUserLeftEvent(Event_UserLeft *event) diff --git a/cockatrice/src/tab_server.h b/cockatrice/src/tab_server.h index 0bdf4570..6093f860 100644 --- a/cockatrice/src/tab_server.h +++ b/cockatrice/src/tab_server.h @@ -46,6 +46,7 @@ signals: void roomJoined(ServerInfo_Room *info, bool setCurrent); void openMessageDialog(const QString &userName, bool focus); void userLeft(const QString &userName); + void userJoined(const QString &userName); private slots: void processListUsersResponse(ProtocolResponse *response); void processUserJoinedEvent(Event_UserJoined *event); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index 70f712b2..d5f37fdf 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -66,6 +66,7 @@ void TabSupervisor::start(AbstractClient *_client, ServerInfo_User *userInfo) tabServer = new TabServer(client, userInfo); connect(tabServer, SIGNAL(roomJoined(ServerInfo_Room *, bool)), this, SLOT(addRoomTab(ServerInfo_Room *, bool))); connect(tabServer, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool))); + connect(tabServer, SIGNAL(userJoined(const QString &)), this, SLOT(processUserJoined(const QString &))); connect(tabServer, SIGNAL(userLeft(const QString &)), this, SLOT(processUserLeft(const QString &))); myAddTab(tabServer); updatePingTime(0, -1); @@ -128,6 +129,11 @@ void TabSupervisor::stop() while (gameIterator.hasNext()) gameIterator.next().value()->deleteLater(); gameTabs.clear(); + + QMapIterator messageIterator(messageTabs); + while (messageIterator.hasNext()) + messageIterator.next().value()->deleteLater(); + messageTabs.clear(); } void TabSupervisor::updatePingTime(int value, int max) @@ -178,6 +184,7 @@ void TabSupervisor::addRoomTab(ServerInfo_Room *info, bool setCurrent) { TabRoom *tab = new TabRoom(client, userName, info); connect(tab, SIGNAL(roomClosing(TabRoom *)), this, SLOT(roomLeft(TabRoom *))); + connect(tab, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool))); myAddTab(tab); roomTabs.insert(info->getRoomId(), tab); if (setCurrent) @@ -197,7 +204,7 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus if (receiverName == userName) return 0; - TabMessage *tab = new TabMessage(client, receiverName); + TabMessage *tab = new TabMessage(client, userName, receiverName); connect(tab, SIGNAL(talkClosing(TabMessage *)), this, SLOT(talkLeft(TabMessage *))); myAddTab(tab); messageTabs.insert(receiverName, tab); @@ -257,7 +264,14 @@ void TabSupervisor::processUserLeft(const QString &userName) { TabMessage *tab = messageTabs.value(userName); if (tab) - tab->processUserLeft(userName); + tab->processUserLeft(); +} + +void TabSupervisor::processUserJoined(const QString &userName) +{ + TabMessage *tab = messageTabs.value(userName); + if (tab) + tab->processUserJoined(); } void TabSupervisor::updateCurrent(int index) diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index f10140ec..0dab9085 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -55,6 +55,7 @@ private slots: void roomLeft(TabRoom *tab); TabMessage *addMessageTab(const QString &userName, bool focus); void processUserLeft(const QString &userName); + void processUserJoined(const QString &userName); void talkLeft(TabMessage *tab); void tabUserEvent(); void processRoomEvent(RoomEvent *event); diff --git a/cockatrice/src/tablezone.cpp b/cockatrice/src/tablezone.cpp index bb74b403..fbb6d044 100644 --- a/cockatrice/src/tablezone.cpp +++ b/cockatrice/src/tablezone.cpp @@ -140,7 +140,7 @@ void TableZone::reorganizeCards() qreal actualX = x + numberAttachedCards * CARD_WIDTH / 3.0; qreal actualY = y; if (numberAttachedCards) - actualY += 5; + actualY += 15; cards[i]->setPos(actualX, actualY); cards[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100); @@ -151,7 +151,7 @@ void TableZone::reorganizeCards() ++j; CardItem *attachedCard = attachedCardIterator.next(); qreal childX = actualX - j * CARD_WIDTH / 3.0; - qreal childY = y - 5; + qreal childY = y + 5; attachedCard->setPos(childX, childY); attachedCard->setRealZValue((childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100); diff --git a/common/server_cardzone.cpp b/common/server_cardzone.cpp index 2d417390..f104478c 100644 --- a/common/server_cardzone.cpp +++ b/common/server_cardzone.cpp @@ -154,9 +154,9 @@ bool Server_CardZone::isColumnEmpty(int x, int y) const void Server_CardZone::moveCard(CommandContainer *cont, QMap &coordMap, Server_Card *card, int x, int y) { - coordMap.remove(card->getX()); - player->moveCard(cont, this, QList() << card->getId(), this, x, y, card->getFaceDown(), false); - coordMap.insert(x, card); + coordMap.remove(card->getY() * 10000 + card->getX()); + player->moveCard(cont, this, QList() << card->getId(), this, x, y, card->getFaceDown(), false, false); + coordMap.insert(y * 10000 + x, card); } void Server_CardZone::fixFreeSpaces(CommandContainer *cont) diff --git a/common/server_player.cpp b/common/server_player.cpp index 7b1c233e..82ac831a 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -233,7 +233,7 @@ public: } }; -ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList &_cardIds, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped) +ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList &_cardIds, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped, bool fixFreeSpaces) { // Disallow controller change to other zones than the table. if (((targetzone->getType() != PublicZone) || !targetzone->hasCoords()) && (startzone->getPlayer() != targetzone->getPlayer())) @@ -302,7 +302,7 @@ ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *st } } - if (card->getDestroyOnZoneChange() && (startzone != targetzone)) { + if (card->getDestroyOnZoneChange() && (startzone->getName() != targetzone->getName())) { cont->enqueueGameEventPrivate(new Event_DestroyCard(getPlayerId(), startzone->getName(), card->getId()), game->getGameId()); cont->enqueueGameEventPublic(new Event_DestroyCard(getPlayerId(), startzone->getName(), card->getId()), game->getGameId()); card->deleteLater(); @@ -369,7 +369,7 @@ ResponseCode Server_Player::moveCard(CommandContainer *cont, Server_CardZone *st setCardAttrHelper(cont, targetzone->getName(), card->getId(), "tapped", "1"); } } - if (startzone->hasCoords()) + if (startzone->hasCoords() && fixFreeSpaces) startzone->fixFreeSpaces(cont); return RespOk; @@ -383,7 +383,7 @@ void Server_Player::unattachCard(CommandContainer *cont, Server_Card *card) cont->enqueueGameEventPrivate(new Event_AttachCard(getPlayerId(), zone->getName(), card->getId(), -1, QString(), -1), game->getGameId()); cont->enqueueGameEventPublic(new Event_AttachCard(getPlayerId(), zone->getName(), card->getId(), -1, QString(), -1), game->getGameId()); - moveCard(cont, zone, QList() << card->getId(), zone, -1, card->getY(), card->getFaceDown(), card->getTapped()); + moveCard(cont, zone, QList() << card->getId(), zone, -1, card->getY(), card->getFaceDown(), false); } ResponseCode Server_Player::setCardAttrHelper(CommandContainer *cont, const QString &zoneName, int cardId, const QString &attrName, const QString &attrValue) diff --git a/common/server_player.h b/common/server_player.h index 5bd08d96..24eebf01 100644 --- a/common/server_player.h +++ b/common/server_player.h @@ -76,7 +76,7 @@ public: void setupZones(); ResponseCode moveCard(CommandContainer *cont, const QString &_startZone, const QList &_cardId, int _targetPlayer, const QString &_targetZone, int _x, int _y, bool _faceDown, bool _tapped); - ResponseCode moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList &_cardId, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped); + ResponseCode moveCard(CommandContainer *cont, Server_CardZone *startzone, const QList &_cardId, Server_CardZone *targetzone, int x, int y, bool faceDown, bool tapped, bool fixFreeSpaces = true); void unattachCard(CommandContainer *cont, Server_Card *card); ResponseCode setCardAttrHelper(CommandContainer *cont, const QString &zone, int cardId, const QString &attrName, const QString &attrValue); diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 0fa91781..b8c9ab48 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -129,7 +129,6 @@ ResponseCode Server_ProtocolHandler::processCommandHelper(Command *command, Comm default: return RespInvalidCommand; } } - qDebug() << "received generic Command"; switch (command->getItemId()) { case ItemId_Command_Ping: return cmdPing(static_cast(command), cont); case ItemId_Command_Login: return cmdLogin(static_cast(command), cont); @@ -659,7 +658,8 @@ ResponseCode Server_ProtocolHandler::cmdAttachCard(Command_AttachCard *cmd, Comm if (targetCard) { // Unattach all cards attached to the card being attached. - const QList &attachedList = card->getAttachedCards(); + // Make a copy of the list because its contents change during the loop otherwise. + QList attachedList = card->getAttachedCards(); for (int i = 0; i < attachedList.size(); ++i) player->unattachCard(cont, attachedList[i]); @@ -670,6 +670,7 @@ ResponseCode Server_ProtocolHandler::cmdAttachCard(Command_AttachCard *cmd, Comm card->setCoords(-1, card->getY()); cont->enqueueGameEventPrivate(new Event_AttachCard(player->getPlayerId(), startzone->getName(), card->getId(), targetPlayer->getPlayerId(), targetzone->getName(), targetCard->getId()), game->getGameId()); cont->enqueueGameEventPublic(new Event_AttachCard(player->getPlayerId(), startzone->getName(), card->getId(), targetPlayer->getPlayerId(), targetzone->getName(), targetCard->getId()), game->getGameId()); + startzone->fixFreeSpaces(cont); } else player->unattachCard(cont, card); diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index c2637d09..f7fbac19 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -225,4 +225,4 @@ void Servatrice::statusUpdate() execSqlQuery(query); } -const QString Servatrice::versionString = "Servatrice 0.20110114"; +const QString Servatrice::versionString = "Servatrice 0.20110124";