#include "chatroom.h" #include "ui_chatroom.h" list sendQueue; std::mutex myMutex; std::mutex msgMutex; std::mutex printMutex; unique_lock* msgLock; std::condition_variable msgListNotEmpty; void sendThread(Socket* s, Chatroom* chat) { string send; while(chat->getConnected()) { while(sendQueue.empty()) { msgLock->lock(); msgListNotEmpty.wait(*msgLock); msgLock->unlock(); if(!chat->getConnected()) { return; } } msgLock->lock(); send = sendQueue.front().toStdString(); sendQueue.pop_front(); try { if(send[0] == '@') { chat->relayMsg(send); send = '1' + send; } else { send = '0' + send; if(send.substr(1) != "/exit" && send.substr(1) != "/disconnect") { chat->putMsgToPrintQueue(chat->getNickname().toStdString().append(": ").append(send.substr(1))); } } msgLock->unlock(); *s << send; send = send.substr(1); if(send == "/disconnect" || send == "/exit") { break; } } catch(SocketException& e) { cout << e.description() << endl; } } } void recvThread(Socket* s, Chatroom* chat) { string recv; while(true) { try { *s >> recv; if(recv[0] == '1') { chat->relayMsg(recv.substr(1)); continue; } else if(recv[0] == '0') { recv = recv.substr(1); } } catch(SocketException &e) { chat->setConnected(false); cout << e.description() << endl; msgListNotEmpty.notify_all(); chat->close(); break; } if(recv == "DISC_OK") { cout << "Disconnecting" << endl; chat->setConnected(false); msgListNotEmpty.notify_all(); break; } else if(recv == "EXIT_OK") { cout << "Exiting" << endl; chat->setConnected(false); msgListNotEmpty.notify_all(); chat->close(); break; } else if(recv.substr(0,8) == "USERLIST") { QString userlist = QString::fromStdString(recv.substr(8)); chat->updateUserList(userlist); } else { chat->putMsgToPrintQueue(recv); } } } Chatroom::Chatroom(QWidget *parent) : QMainWindow(parent), ui(new Ui::Chatroom) { ui->setupUi(this); QList chatSizes; QList splitSizes; chatSizes.push_front(ui->chatText->height()); chatSizes.push_front(100); splitSizes.push_front(ui->chatWindow->width()); splitSizes.push_front(200); ui->chatSplitter->setSizes(chatSizes); ui->windowSplitter->setSizes(splitSizes); connected = false; msgLock = new unique_lock(msgMutex,std::defer_lock); chatLock = new unique_lock(chatMutex,std::defer_lock); send = NULL; recv = NULL; connect(ui->actionConnect,SIGNAL(triggered()),this,SLOT(startSession())); connect(ui->actionExit,SIGNAL(triggered()),this,SLOT(close())); connect(ui->actionDisconnect,SIGNAL(triggered()),this,SLOT(disconnectChatroom())); connect(ui->actionSave_chat,SIGNAL(triggered()),ui->chatText,SLOT(saveChatToFile())); connect(ui->inputText,SIGNAL(msgReady()),this,SLOT(sendMsg())); connect(ui->sendButton,SIGNAL(clicked()),this,SLOT(sendMsg())); connect(this,SIGNAL(messagesToPrint()),this,SLOT(printMsg())); connect(ui->userList,SIGNAL(doubleClicked(QModelIndex)),this,SLOT(launchChatWindow(QModelIndex))); connect(this,SIGNAL(createChat(QString)),this,SLOT(newChat(QString))); } void Chatroom::closeEvent(QCloseEvent *event) { if(recv == NULL && send == NULL) { ui->inputText->setReadOnly(true); ui->chatText->printServerMsg("Disconnected"); event->accept(); return; } sendQueue.clear(); QString exitMsg("/exit"); putMsgToSendQueue(exitMsg); ui->inputText->setReadOnly(true); recv->join(); send->join(); ui->chatText->printServerMsg("Disconnected"); send = NULL; recv = NULL; event->accept(); } bool Chatroom::getConnected() { myMutex.lock(); bool val = connected; myMutex.unlock(); return val; } void Chatroom::setConnected(bool status) { myMutex.lock(); connected = status; myMutex.unlock(); } void Chatroom::setNickname(QString nick) { nickname = nick; } QString Chatroom::getNickname() { return nickname; } void Chatroom::updateUserList(QString userlist) { ui->userList->clear(); QStringList users = userlist.split('\n',QString::SkipEmptyParts); for(int i = 0; i < users.size(); i++) { ui->userList->addItem(users[i]); } } void Chatroom::relayMsg(string msg) { string sender = getSender(msg); if(!ui->userList->findItems(QString::fromStdString(sender),Qt::MatchExactly | Qt::MatchCaseSensitive).isEmpty()) { map::iterator it = activeChats.begin(); it = activeChats.find(sender); if(it == activeChats.end()) { chatLock->lock(); emit createChat(QString::fromStdString(sender)); chatCreated.wait(*chatLock); chatLock->unlock(); it = activeChats.find(sender); } ((ChatWindow*)it->second)->notifyPrint(msg); } } void Chatroom::newChat(QString peerNick) { ChatWindow* newchat = new ChatWindow(peerNick,this); activeChats.insert(std::pair(peerNick.toStdString(),newchat)); newchat->show(); chatCreated.notify_all(); } void Chatroom::removeChat(QString &nickname) { ChatWindow* chat = (ChatWindow*)activeChats.find(nickname.toStdString())->second; delete chat; activeChats.erase(nickname.toStdString()); } void Chatroom::launchChatWindow(QModelIndex index) { QString peerNick = ui->userList->model()->data(index).toString(); if(activeChats.find(peerNick.toStdString()) == activeChats.end()) { emit newChat(peerNick); } } void Chatroom::putMsgToPrintQueue(string &msg) { printMutex.lock(); printQueue.push_back(msg); emit messagesToPrint(); } void Chatroom::printMsg() { while(!printQueue.empty()) { string msg = printQueue.front(); ui->chatText->printMsg(msg); printQueue.pop_front(); } printMutex.unlock(); } void Chatroom::putMsgToSendQueue(QString& msg) { msgMutex.lock(); sendQueue.push_back(msg); msgMutex.unlock(); msgListNotEmpty.notify_all(); } void Chatroom::sendMsg() { QString msg = ui->inputText->toPlainText(); ui->inputText->clear(); if(msg == "/disconnect") { this->disconnectChatroom(); } else if(msg == "/exit") { this->close(); } else { putMsgToSendQueue(msg); } } string Chatroom::getSender(string msg) { cout << msg << endl; if(msg[0] == '@') { return msg.substr(1,msg.find(" ")-1); } else { return msg.substr(0,msg.find(":")); } } void Chatroom::disconnectChatroom() { msgMutex.lock(); sendQueue.clear(); sendQueue.push_back("/disconnect"); ui->chatText->printServerMsg("Disconnected"); this->ui->inputText->setReadOnly(true); this->ui->sendButton->setDisabled(true); this->ui->actionDisconnect->setDisabled(true); msgMutex.unlock(); msgListNotEmpty.notify_all(); } void Chatroom::startSession() { LoginScreen login(&s,this); login.exec(); int result = login.result(); if(result == QDialog::Accepted) { ui->inputText->setReadOnly(false); } else if(result == QDialog::Rejected) { return; } connected = true; this->ui->inputText->setReadOnly(false); this->ui->sendButton->setDisabled(false); this->ui->actionDisconnect->setDisabled(false); ui->chatText->printServerMsg("Connected to chatroom"); recv = new std::thread(recvThread,&s,this); send = new std::thread(sendThread,&s,this); } Chatroom::~Chatroom() { delete msgLock; delete recv; delete send; map::iterator it; for(it = activeChats.begin(); it != activeChats.end(); it++) { ChatWindow* chat = (ChatWindow*) it->second; delete chat; } delete ui; }