chatroom.cpp 8.53 KB
#include "chatroom.h"
#include "ui_chatroom.h"

list<QString> sendQueue;
std::mutex myMutex;
std::mutex msgMutex;
std::mutex printMutex;
unique_lock<std::mutex>* 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<int> chatSizes;
    QList<int> 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<std::mutex>(msgMutex,std::defer_lock);
    chatLock = new unique_lock<std::mutex>(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<string,void*>::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<string,ChatWindow*>(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<string,void*>::iterator it;
    for(it = activeChats.begin(); it != activeChats.end(); it++)
    {
        ChatWindow* chat = (ChatWindow*) it->second;
        delete chat;
    }
    delete ui;
}