#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)),MSG_USER); } } 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->toggleConnected(false); cout << e.description() << endl; msgListNotEmpty.notify_all(); string msg = "Connection to server lost"; chat->putMsgToPrintQueue(msg,MSG_STATUS); break; } if(recv == "DISC_OK") { cout << "Disconnecting" << endl; chat->toggleConnected(false); msgListNotEmpty.notify_all(); break; } else if(recv == "EXIT_OK") { cout << "Exiting" << endl; chat->toggleConnected(false); msgListNotEmpty.notify_all(); chat->close(); break; } else if(recv.substr(0,8) == "USERLIST") { QString userlist = QString::fromStdString(recv.substr(8)); emit chat->newUsers(userlist); } else { chat->putMsgToPrintQueue(recv,MSG_USER); } } } 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(this,SIGNAL(serverMessagesToPrint()),this,SLOT(printServerMsg())); connect(this,SIGNAL(statusMessagesToPrint()),this,SLOT(printStatusMsg())); connect(ui->userList,SIGNAL(doubleClicked(QModelIndex)),this,SLOT(launchChatWindow(QModelIndex))); connect(this,SIGNAL(createChat(QString)),this,SLOT(newChat(QString))); connect(this,SIGNAL(toggleConnected(bool)),this,SLOT(setConnected(bool))); connect(this,SIGNAL(newUsers(QString)),this,SLOT(updateUserList(QString))); } void Chatroom::closeEvent(QCloseEvent *event) { if(recv == NULL && send == NULL) { event->accept(); return; } sendQueue.clear(); QString exitMsg("/exit"); putMsgToSendQueue(exitMsg); recv->join(); send->join(); freeChats(true); freeChats(false); event->accept(); } bool Chatroom::getConnected() { myMutex.lock(); bool val = connected; myMutex.unlock(); return val; } void Chatroom::setConnected(bool status) { myMutex.lock(); connected = status; if(status == false) { ui->chatText->printServerMsg("Disconnected"); this->ui->inputText->setReadOnly(true); this->ui->sendButton->setDisabled(true); this->ui->actionDisconnect->setDisabled(true); this->ui->userList->clear(); } else if(status == true) { this->ui->inputText->setReadOnly(false); this->ui->sendButton->setDisabled(false); this->ui->actionDisconnect->setDisabled(false); ui->chatText->printServerMsg("Connected to chatroom"); } 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) { freeChats(false); ChatWindow* newchat = new ChatWindow(peerNick,this); activeChats.insert(std::pair(peerNick.toStdString(),newchat)); newchat->show(); chatCreated.notify_all(); } void Chatroom::removeChat(QString &nickname) { chatMutex.lock(); ChatWindow* chat = (ChatWindow*) activeChats.find(nickname.toStdString())->second; inactiveChats.insert(std::pair(nickname.toStdString(),chat)); activeChats.erase(nickname.toStdString()); chatMutex.unlock(); } void Chatroom::launchChatWindow(QModelIndex index) { QString peerNick = ui->userList->model()->data(index).toString(); if(activeChats.find(peerNick.toStdString()) == activeChats.end()) { emit createChat(peerNick); } } void Chatroom::putMsgToPrintQueue(string &msg, int type) { printMutex.lock(); if(type == MSG_USER) { printQueue.push_back(msg); emit messagesToPrint(); return; } else if(type == MSG_SERVER) { printServerQueue.push_back(msg); emit serverMessagesToPrint(); return; } else if(type == MSG_STATUS) { printStatusQueue.push_back(msg); emit statusMessagesToPrint(); return; } } void Chatroom::printMsg() { while(!printQueue.empty()) { string msg = printQueue.front(); ui->chatText->printMsg(msg); printQueue.pop_front(); } printMutex.unlock(); } void Chatroom::printServerMsg() { while(!printServerQueue.empty()) { string msg = printServerQueue.front(); ui->chatText->printServerMsg(msg); printServerQueue.pop_front(); } printMutex.unlock(); } void Chatroom::printStatusMsg() { while(!printStatusQueue.empty()) { string msg = printStatusQueue.front(); ui->chatText->printStatusMsg(msg); printStatusQueue.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) { 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"); msgMutex.unlock(); msgListNotEmpty.notify_all(); } void Chatroom::startSession() { LoginScreen login(&s,this); login.exec(); int result = login.result(); if(result == QDialog::Rejected) { return; } setConnected(true); recv = new std::thread(recvThread,&s,this); send = new std::thread(sendThread,&s,this); } void Chatroom::freeChats(bool active) { map::iterator it; if(active) { while(true) { chatMutex.lock(); it = activeChats.begin(); chatMutex.unlock(); if(it == activeChats.end()) { break; } ChatWindow* chat = (ChatWindow*) it->second; chat->notifyClose(); } } else { for(it = inactiveChats.begin(); it != inactiveChats.end(); it++) { ChatWindow* chat = (ChatWindow*) it->second; delete chat; } inactiveChats.clear(); } } Chatroom::~Chatroom() { delete msgLock; delete chatLock; delete recv; delete send; delete ui; }