#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tab_supervisor.h" #include "tab_room.h" #include "tab_userlists.h" #include "userlist.h" #include "abstractclient.h" #include "chatview.h" #include "gameselector.h" #include "settingscache.h" #include "main.h" #include "get_pb_extension.h" #include "pb/room_commands.pb.h" #include "pb/serverinfo_room.pb.h" #include "pb/event_list_games.pb.h" #include "pb/event_join_room.pb.h" #include "pb/event_leave_room.pb.h" #include "pb/event_room_say.pb.h" #include "pending_command.h" #include "dlg_settings.h" TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerInfo_User *_ownUser, const ServerInfo_Room &info) : Tab(_tabSupervisor), client(_client), roomId(info.room_id()), roomName(QString::fromStdString(info.name())), ownUser(_ownUser) { const int gameTypeListSize = info.gametype_list_size(); for (int i = 0; i < gameTypeListSize; ++i) gameTypes.insert(info.gametype_list(i).game_type_id(), QString::fromStdString(info.gametype_list(i).description())); QMap tempMap; tempMap.insert(info.room_id(), gameTypes); gameSelector = new GameSelector(client, tabSupervisor, this, QMap(), tempMap, true, true); userList = new UserList(tabSupervisor, client, UserList::RoomList); connect(userList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool))); chatView = new ChatView(tabSupervisor, 0, true); connect(chatView, SIGNAL(showMentionPopup(QString&)), this, SLOT(actShowMentionPopup(QString&))); 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))); connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString))); connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged())); sayLabel = new QLabel; sayEdit = new CustomLineEdit; sayLabel->setBuddy(sayEdit); connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage())); QMenu *chatSettingsMenu = new QMenu(this); aClearChat = chatSettingsMenu->addAction(QString()); aClearChat->setShortcut(QKeySequence("F12")); connect(aClearChat, SIGNAL(triggered()), this, SLOT(actClearChat())); chatSettingsMenu->addSeparator(); aOpenChatSettings = chatSettingsMenu->addAction(QString()); connect(aOpenChatSettings, SIGNAL(triggered()), this, SLOT(actOpenChatSettings())); QToolButton *chatSettingsButton = new QToolButton; chatSettingsButton->setIcon(QIcon(":/resources/icon_settings.svg")); chatSettingsButton->setMenu(chatSettingsMenu); chatSettingsButton->setPopupMode(QToolButton::InstantPopup); QHBoxLayout *sayHbox = new QHBoxLayout; sayHbox->addWidget(sayLabel); sayHbox->addWidget(sayEdit); sayHbox->addWidget(chatSettingsButton); QVBoxLayout *chatVbox = new QVBoxLayout; chatVbox->addWidget(chatView); chatVbox->addLayout(sayHbox); chatGroupBox = new QGroupBox; chatGroupBox->setLayout(chatVbox); QSplitter *splitter = new QSplitter(Qt::Vertical); splitter->addWidget(gameSelector); splitter->addWidget(chatGroupBox); QHBoxLayout *hbox = new QHBoxLayout; hbox->addWidget(splitter, 3); hbox->addWidget(userList, 1); aLeaveRoom = new QAction(this); connect(aLeaveRoom, SIGNAL(triggered()), this, SLOT(actLeaveRoom())); roomMenu = new QMenu(this); roomMenu->addAction(aLeaveRoom); addTabMenu(roomMenu); retranslateUi(); setLayout(hbox); const int userListSize = info.user_list_size(); for (int i = 0; i < userListSize; ++i){ userList->processUserInfo(info.user_list(i), true); autocompleteUserList.append("@" + QString::fromStdString(info.user_list(i).name())); } userList->sortItems(); const int gameListSize = info.game_list_size(); for (int i = 0; i < gameListSize; ++i) gameSelector->processGameInfo(info.game_list(i)); completer = new QCompleter(autocompleteUserList, sayEdit); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setMaxVisibleItems(5); #if QT_VERSION >= 0x050000 completer->setFilterMode(Qt::MatchStartsWith); #endif sayEdit->setCompleter(completer); actCompleterChanged(); } TabRoom::~TabRoom() { emit roomClosing(this); } void TabRoom::retranslateUi() { gameSelector->retranslateUi(); chatView->retranslateUi(); userList->retranslateUi(); sayLabel->setText(tr("&Say:")); chatGroupBox->setTitle(tr("Chat")); roomMenu->setTitle(tr("&Room")); aLeaveRoom->setText(tr("&Leave room")); aClearChat->setText(tr("&Clear chat")); aOpenChatSettings->setText(tr("Chat Settings...")); } void TabRoom::focusTab() { QApplication::setActiveWindow(this); tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this)); emit maximizeClient(); } void TabRoom::actShowMentionPopup(QString &sender) { if (tabSupervisor->currentIndex() != tabSupervisor->indexOf(this) || QApplication::activeWindow() == 0 || QApplication::focusWidget() == 0) { disconnect(trayIcon, SIGNAL(messageClicked()), 0, 0); trayIcon->showMessage(sender + tr(" mentioned you."), tr("Click to view")); connect(trayIcon, SIGNAL(messageClicked()), chatView, SLOT(actMessageClicked())); } } void TabRoom::closeRequest() { actLeaveRoom(); } void TabRoom::tabActivated() { if(!sayEdit->hasFocus()) sayEdit->setFocus(); } QString TabRoom::sanitizeHtml(QString dirty) const { return dirty .replace("&", "&") .replace("<", "<") .replace(">", ">"); } void TabRoom::sendMessage() { if (sayEdit->text().isEmpty()){ return; }else if (completer->popup()->isVisible()){ completer->popup()->hide(); return; }else{ Command_RoomSay cmd; cmd.set_message(sayEdit->text().toStdString()); PendingCommand *pend = prepareRoomCommand(cmd); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(sayFinished(const Response &))); sendRoomCommand(pend); sayEdit->clear(); } } void TabRoom::sayFinished(const Response &response) { if (response.response_code() == Response::RespChatFlood) chatView->appendMessage(tr("You are flooding the chat. Please wait a couple of seconds.")); } void TabRoom::actLeaveRoom() { sendRoomCommand(prepareRoomCommand(Command_LeaveRoom())); deleteLater(); } void TabRoom::actClearChat() { chatView->clearChat(); } void TabRoom::actOpenChatSettings() { DlgSettings settings(this); settings.setTab(4); settings.exec(); } void TabRoom::actCompleterChanged() { settingsCache->getChatMentionCompleter() ? completer->setCompletionRole(2) : completer->setCompletionRole(1); } void TabRoom::processRoomEvent(const RoomEvent &event) { switch (static_cast(getPbExtension(event))) { case RoomEvent::LIST_GAMES: processListGamesEvent(event.GetExtension(Event_ListGames::ext)); break; case RoomEvent::JOIN_ROOM: processJoinRoomEvent(event.GetExtension(Event_JoinRoom::ext)); break; case RoomEvent::LEAVE_ROOM: processLeaveRoomEvent(event.GetExtension(Event_LeaveRoom::ext)); break; case RoomEvent::ROOM_SAY: processRoomSayEvent(event.GetExtension(Event_RoomSay::ext)); break; default: ; } } void TabRoom::processListGamesEvent(const Event_ListGames &event) { const int gameListSize = event.game_list_size(); for (int i = 0; i < gameListSize; ++i) gameSelector->processGameInfo(event.game_list(i)); } void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event) { userList->processUserInfo(event.user_info(), true); userList->sortItems(); if (!autocompleteUserList.contains("@" + QString::fromStdString(event.user_info().name()))){ autocompleteUserList << "@" + QString::fromStdString(event.user_info().name()); sayEdit->updateCompleterModel(autocompleteUserList); } } void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event) { userList->deleteUser(QString::fromStdString(event.name())); autocompleteUserList.removeOne("@" + QString::fromStdString(event.name())); sayEdit->updateCompleterModel(autocompleteUserList); } void TabRoom::processRoomSayEvent(const Event_RoomSay &event) { QString senderName = QString::fromStdString(event.name()); if (tabSupervisor->getUserListsTab()->getIgnoreList()->getUsers().contains(senderName)) return; UserListTWI *twi = userList->getUsers().value(senderName); UserLevelFlags userLevel; if (twi) { userLevel = UserLevelFlags(twi->getUserInfo().user_level()); if (settingsCache->getIgnoreUnregisteredUsers() && !userLevel.testFlag(ServerInfo_User::IsRegistered)) return; } chatView->appendMessage(QString::fromStdString(event.message()), senderName, userLevel, true); emit userEvent(false); } void TabRoom::addMentionTag(QString mentionTag) { sayEdit->insert(mentionTag + " "); sayEdit->setFocus(); } PendingCommand *TabRoom::prepareRoomCommand(const ::google::protobuf::Message &cmd) { return client->prepareRoomCommand(cmd, roomId); } void TabRoom::sendRoomCommand(PendingCommand *pend) { client->sendCommand(pend); } CustomLineEdit::CustomLineEdit(QWidget *parent) : QLineEdit(parent) { } void CustomLineEdit::focusOutEvent(QFocusEvent * e){ QLineEdit::focusOutEvent(e); if (c->popup()->isVisible()){ //Remove Popup c->popup()->hide(); //Truncate the line to last space or whole string QString textValue = text(); int lastIndex = textValue.length(); int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; int leftShift = qMin(lastIndex, lastWordStartIndex); setText(textValue.left(leftShift)); //Insert highlighted line from popup insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); //Set focus back to the textbox since tab was pressed setFocus(); } } void CustomLineEdit::keyPressEvent(QKeyEvent * event) { switch (event->key()){ case Qt::Key_Return: case Qt::Key_Enter: case Qt::Key_Escape: if (c->popup()->isVisible()){ event->ignore(); //Remove Popup c->popup()->hide(); //Truncate the line to last space or whole string QString textValue = text(); int lastIndexof = textValue.lastIndexOf(" "); QString finalString = textValue.left(lastIndexof); //Add a space if there's a word if (finalString != "") finalString += " "; setText(finalString); return; } break; case Qt::Key_Space: if (c->popup()->isVisible()){ event->ignore(); //Remove Popup c->popup()->hide(); //Truncate the line to last space or whole string QString textValue = text(); int lastIndex = textValue.length(); int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; int leftShift = qMin(lastIndex, lastWordStartIndex); setText(textValue.left(leftShift)); //Insert highlighted line from popup insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); return; } break; default: break; } QLineEdit::keyPressEvent(event); //Wait until the first character after @ if (!c || text().right(1).contains("@")) return; //Set new completion prefix c->setCompletionPrefix(cursorWord(text())); if (c->completionPrefix().length() < 1){ c->popup()->hide(); return; } //Draw completion box QRect cr = cursorRect(); cr.setWidth(c->popup()->sizeHintForColumn(0) + c->popup()->verticalScrollBar()->sizeHint().width()); c->complete(cr); //Select first item in the completion popup QItemSelectionModel* sm = new QItemSelectionModel(c->completionModel()); c->popup()->setSelectionModel(sm); sm->select(c->completionModel()->index(0, 0), QItemSelectionModel::ClearAndSelect); sm->setCurrentIndex(c->completionModel()->index(0, 0), QItemSelectionModel::NoUpdate); } QString CustomLineEdit::cursorWord(const QString &line) const { return line.mid(line.left(cursorPosition()).lastIndexOf(" ") + 1, cursorPosition() - line.left(cursorPosition()).lastIndexOf(" ") - 1); } void CustomLineEdit::insertCompletion(QString arg) { QString s_arg = arg + " "; setText(text().replace(text().left(cursorPosition()).lastIndexOf(" ") + 1, cursorPosition() - text().left(cursorPosition()).lastIndexOf(" ") - 1, s_arg)); } void CustomLineEdit::setCompleter(QCompleter* completer) { c = completer; c->setWidget(this); connect(c, SIGNAL(activated(QString)),this, SLOT(insertCompletion(QString))); } void CustomLineEdit::updateCompleterModel(QStringList completionList) { if (!c || c->popup()->isVisible()) return; QStringListModel *model; model = (QStringListModel*)(c->model()); if (model == NULL) model = new QStringListModel(); QStringList updatedList = completionList; model->setStringList(updatedList); }