diff --git a/qtox.pro b/qtox.pro index 679e6d17e..2e8468c07 100644 --- a/qtox.pro +++ b/qtox.pro @@ -101,7 +101,8 @@ HEADERS += widget/form/addfriendform.h \ widget/adjustingscrollarea.h \ widget/croppinglabel.h \ widget/friendlistwidget.h \ - widget/genericchatroomwidget.h + widget/genericchatroomwidget.h \ + widget/form/genericchatform.h SOURCES += \ widget/form/addfriendform.cpp \ @@ -134,4 +135,5 @@ SOURCES += \ widget/croppinglabel.cpp \ widget/friendlistwidget.cpp \ coreav.cpp \ - widget/genericchatroomwidget.cpp + widget/genericchatroomwidget.cpp \ + widget/form/genericchatform.cpp diff --git a/res.qrc b/res.qrc index f413e0209..0b6abfff5 100644 --- a/res.qrc +++ b/res.qrc @@ -124,5 +124,6 @@ ui/micButton/micButtonPressed.png ui/micButton/micButton.css ui/volButton/volButton.css + ui/fileButton/fileButtonDisabled.png diff --git a/ui/fileButton/fileButton.css b/ui/fileButton/fileButton.css index 59b110ced..fd06c6f0e 100644 --- a/ui/fileButton/fileButton.css +++ b/ui/fileButton/fileButton.css @@ -17,6 +17,11 @@ QPushButton:pressed background-image: url(":/ui/fileButton/fileButtonPressed.png"); } +QPushButton[enabled="false"] +{ + background-image: url(":/ui/fileButton/fileButtonDisabled.png"); +} + QPushButton:focus { outline: none; } diff --git a/ui/fileButton/fileButtonDisabled.png b/ui/fileButton/fileButtonDisabled.png new file mode 100644 index 000000000..ed024e2df Binary files /dev/null and b/ui/fileButton/fileButtonDisabled.png differ diff --git a/widget/form/chatform.cpp b/widget/form/chatform.cpp index 50f09f5a2..141451c4c 100644 --- a/widget/form/chatform.cpp +++ b/widget/form/chatform.cpp @@ -16,140 +16,24 @@ #include "chatform.h" #include "friend.h" -#include "smileypack.h" #include "widget/friendwidget.h" #include "widget/widget.h" #include "widget/filetransfertwidget.h" -#include "widget/emoticonswidget.h" -#include "style.h" -#include -#include #include #include -#include -#include -#include #include ChatForm::ChatForm(Friend* chatFriend) - : f(chatFriend), curRow{0}, lockSliderToBottom{true} + : f(chatFriend) { - main = new QWidget(), head = new QWidget(), chatAreaWidget = new QWidget(); - name = new QLabel(), avatar = new QLabel(), statusMessage = new QLabel(); - headLayout = new QHBoxLayout(), mainFootLayout = new QHBoxLayout(); - headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout(), - footButtonsSmall = new QVBoxLayout(), volMicLayout = new QVBoxLayout(); - mainChatLayout = new QGridLayout(); - msgEdit = new ChatTextEdit(); - sendButton = new QPushButton(), fileButton = new QPushButton(), emoteButton = new QPushButton(), - callButton = new QPushButton(), videoButton = new QPushButton(), - volButton = new QPushButton(), micButton = new QPushButton(); - chatArea = new QScrollArea(); + nameLabel->setText(f->getName()); + avatarLabel->setPixmap(QPixmap(":/img/contact_dark.png")); + + statusMessageLabel = new QLabel(); netcam = new NetCamView(); - audioInputFlag = false; - - QFont bold; - bold.setBold(true); - name->setText(chatFriend->widget->name.text()); - name->setFont(bold); - statusMessage->setText(chatFriend->widget->statusMessage.text()); - - // No real avatar support in toxcore, better draw a pretty picture - //avatar->setPixmap(*chatFriend->widget->avatar.pixmap()); - avatar->setPixmap(QPixmap(":/img/contact_dark.png")); - - chatAreaWidget->setLayout(mainChatLayout); - chatAreaWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css")); - - chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - chatArea->setWidgetResizable(true); - chatArea->setContextMenuPolicy(Qt::CustomContextMenu); - chatArea->setFrameStyle(QFrame::NoFrame); - - mainChatLayout->setColumnStretch(1,1); - mainChatLayout->setSpacing(5); - - footButtonsSmall->setSpacing(2); - - msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css")); - msgEdit->setFixedHeight(50); - msgEdit->setFrameStyle(QFrame::NoFrame); - - sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css")); - fileButton->setStyleSheet(Style::get(":/ui/fileButton/fileButton.css")); - emoteButton->setStyleSheet(Style::get(":/ui/emoteButton/emoteButton.css")); - - callButton->setObjectName("green"); - callButton->setStyleSheet(Style::get(":/ui/callButton/callButton.css")); - - videoButton->setObjectName("green"); - videoButton->setStyleSheet(Style::get(":/ui/videoButton/videoButton.css")); - - QString volButtonStylesheet = ""; - try - { - QFile f(":/ui/volButton/volButton.css"); - f.open(QFile::ReadOnly | QFile::Text); - QTextStream volButtonStylesheetStream(&f); - volButtonStylesheet = volButtonStylesheetStream.readAll(); - } - catch (int e) {} - volButton->setObjectName("green"); - volButton->setStyleSheet(volButtonStylesheet); - - QString micButtonStylesheet = ""; - try - { - QFile f(":/ui/micButton/micButton.css"); - f.open(QFile::ReadOnly | QFile::Text); - QTextStream micButtonStylesheetStream(&f); - micButtonStylesheet = micButtonStylesheetStream.readAll(); - } - catch (int e) {} - micButton->setObjectName("green"); - micButton->setStyleSheet(micButtonStylesheet); - - main->setLayout(mainLayout); - mainLayout->addWidget(chatArea); - mainLayout->addLayout(mainFootLayout); - mainLayout->setMargin(0); - - footButtonsSmall->addWidget(emoteButton); - footButtonsSmall->addWidget(fileButton); - - mainFootLayout->addWidget(msgEdit); - mainFootLayout->addLayout(footButtonsSmall); - mainFootLayout->addSpacing(5); - mainFootLayout->addWidget(sendButton); - mainFootLayout->setSpacing(0); - - head->setLayout(headLayout); - headLayout->addWidget(avatar); - headLayout->addLayout(headTextLayout); - headLayout->addStretch(); - headLayout->addLayout(volMicLayout); - headLayout->addWidget(callButton); - headLayout->addWidget(videoButton); - - volMicLayout->addWidget(micButton); - volMicLayout->addWidget(volButton); + headTextLayout->addWidget(statusMessageLabel); headTextLayout->addStretch(); - headTextLayout->addWidget(name); - headTextLayout->addWidget(statusMessage); - headTextLayout->addStretch(); - - chatArea->setWidget(chatAreaWidget); - - //Fix for incorrect layouts on OS X as per - //https://bugreports.qt-project.org/browse/QTBUG-14591 - sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); - fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); - emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); - // callButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); - // videoButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); - // msgEdit->setAttribute(Qt::WA_LayoutUsesWidgetRect); - // chatArea->setAttribute(Qt::WA_LayoutUsesWidgetRect); connect(Widget::getInstance()->getCore(), &Core::fileSendStarted, this, &ChatForm::startFileSend); connect(Widget::getInstance()->getCore(), &Core::videoFrameReceived, netcam, &NetCamView::updateDisplay); @@ -158,37 +42,18 @@ ChatForm::ChatForm(Friend* chatFriend) connect(callButton, &QPushButton::clicked, this, &ChatForm::onCallTriggered); connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered); connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered); - connect(chatArea->verticalScrollBar(), &QScrollBar::rangeChanged, this, &ChatForm::onSliderRangeChanged); - connect(chatArea, &QScrollArea::customContextMenuRequested, this, &ChatForm::onChatContextMenuRequested); - connect(emoteButton, &QPushButton::clicked, this, &ChatForm::onEmoteButtonClicked); connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle())); } ChatForm::~ChatForm() { - delete main; - delete head; delete netcam; } -void ChatForm::show(Ui::MainWindow &ui) -{ - ui.mainContent->layout()->addWidget(main); - ui.mainHead->layout()->addWidget(head); - main->show(); - head->show(); -} - -void ChatForm::setName(QString newName) -{ - name->setText(newName); - name->setToolTip(newName); // for overlength names -} - void ChatForm::setStatusMessage(QString newMessage) { - statusMessage->setText(newMessage); - statusMessage->setToolTip(newMessage); // for overlength messsages + statusMessageLabel->setText(newMessage); + statusMessageLabel->setToolTip(newMessage); // for overlength messsages } void ChatForm::onSendTriggered() @@ -202,81 +67,6 @@ void ChatForm::onSendTriggered() emit sendMessage(f->friendId, msg); } -void ChatForm::addFriendMessage(QString message) -{ - QLabel *msgAuthor = new QLabel(name->text()); - QLabel *msgText = new QLabel(message); - QLabel *msgDate = new QLabel(QTime::currentTime().toString("hh:mm")); - - addMessage(msgAuthor, msgText, msgDate); -} - -void ChatForm::addMessage(QString author, QString message, QString date) -{ - addMessage(new QLabel(author), new QLabel(message), new QLabel(date)); -} - -void ChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date) -{ - QScrollBar* scroll = chatArea->verticalScrollBar(); - lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); - author->setAlignment(Qt::AlignTop | Qt::AlignRight); - date->setAlignment(Qt::AlignTop); - message->setWordWrap(true); - message->setTextInteractionFlags(Qt::TextBrowserInteraction); - author->setTextInteractionFlags(Qt::TextBrowserInteraction); - date->setTextInteractionFlags(Qt::TextBrowserInteraction); - if (author->text() == Widget::getInstance()->getUsername()) - { - QPalette pal; - pal.setColor(QPalette::WindowText, QColor(100,100,100)); - author->setPalette(pal); - message->setPalette(pal); - } - if (previousName.isEmpty() || previousName != author->text()) - { - if (curRow) - { - mainChatLayout->setRowStretch(curRow, 0); - mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3); - } - previousName = author->text(); - curRow++; - } - else if (curRow)// onSaveLogClicked expects 0 or 3 QLabel per line - author->setText(""); - - QColor greentext(61,204,61); - QString fontTemplate = "%2"; - - QString finalMessage; - QStringList messageLines = message->text().split("\n"); - for (QString& s : messageLines) - { - if (QRegExp("^[ ]*>.*").exactMatch(s)) - finalMessage += fontTemplate.arg(greentext.name(), s.replace(" ", " ")); - else - finalMessage += s.replace(" ", " "); - finalMessage += "
"; - } - message->setText(finalMessage.left(finalMessage.length()-4)); - message->setText(SmileyPack::getInstance().smileyfied(message->text())); - message->setTextFormat(Qt::RichText); - - mainChatLayout->addWidget(author, curRow, 0); - mainChatLayout->addWidget(message, curRow, 1); - mainChatLayout->addWidget(date, curRow, 3); - mainChatLayout->setRowStretch(curRow+1, 1); - mainChatLayout->setRowStretch(curRow, 0); - curRow++; - author->setContextMenuPolicy(Qt::CustomContextMenu); - message->setContextMenuPolicy(Qt::CustomContextMenu); - date->setContextMenuPolicy(Qt::CustomContextMenu); - connect(author, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); - connect(message, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); - connect(date, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); -} - void ChatForm::onAttachClicked() { QString path = QFileDialog::getOpenFileName(0,tr("Send a file")); @@ -299,13 +89,6 @@ void ChatForm::onAttachClicked() emit sendFile(f->friendId, fi.fileName(), path, filesize); } -void ChatForm::onSliderRangeChanged() -{ - QScrollBar* scroll = chatArea->verticalScrollBar(); - if (lockSliderToBottom) - scroll->setValue(scroll->maximum()); -} - void ChatForm::startFileSend(ToxFile file) { if (file.friendId != f->friendId) @@ -661,74 +444,6 @@ void ChatForm::onCancelCallTriggered() emit cancelCall(callId, f->friendId); } -void ChatForm::onChatContextMenuRequested(QPoint pos) -{ - QWidget* sender = (QWidget*)QObject::sender(); - pos = sender->mapToGlobal(pos); - QMenu menu; - menu.addAction(tr("Save chat log"), this, SLOT(onSaveLogClicked())); - menu.exec(pos); -} - -void ChatForm::onSaveLogClicked() -{ - QString path = QFileDialog::getSaveFileName(0,tr("Save chat log")); - if (path.isEmpty()) - return; - - QFile file(path); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) - return; - - QString log; - QList labels = chatAreaWidget->findChildren(); - int i=0; - for (QLabel* label : labels) - { - log += label->text(); - if (i==2) - { - i=0; - log += '\n'; - } - else - { - log += '\t'; - i++; - } - } - - file.write(log.toUtf8()); - file.close(); -} - -void ChatForm::onEmoteButtonClicked() -{ - // don't show the smiley selection widget if there are no smileys available - if (SmileyPack::getInstance().getEmoticons().empty()) - return; - - EmoticonsWidget widget; - connect(&widget, &EmoticonsWidget::insertEmoticon, this, &ChatForm::onEmoteInsertRequested); - - QWidget* sender = qobject_cast(QObject::sender()); - if (sender) - { - QPoint pos = -QPoint(widget.sizeHint().width() / 2, widget.sizeHint().height()) - QPoint(0, 10); - widget.exec(sender->mapToGlobal(pos)); - } -} - -void ChatForm::onEmoteInsertRequested(QString str) -{ - // insert the emoticon - QWidget* sender = qobject_cast(QObject::sender()); - if (sender) - msgEdit->insertPlainText(str); - - msgEdit->setFocus(); // refocus so that we can continue typing -} - void ChatForm::onMicMuteToggle() { if (audioInputFlag == true) diff --git a/widget/form/chatform.h b/widget/form/chatform.h index dd746d6c1..6a7a9362d 100644 --- a/widget/form/chatform.h +++ b/widget/form/chatform.h @@ -17,41 +17,21 @@ #ifndef CHATFORM_H #define CHATFORM_H -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "widget/tool/chattextedit.h" -#include "ui_mainwindow.h" +#include "genericchatform.h" #include "core.h" #include "widget/netcamview.h" -// Spacing in px inserted when the author of the last message changes -#define AUTHOR_CHANGE_SPACING 5 - struct Friend; -class ChatForm : public QObject +class ChatForm : public GenericChatForm { Q_OBJECT public: ChatForm(Friend* chatFriend); ~ChatForm(); - void show(Ui::MainWindow &ui); - void setName(QString newName); void setStatusMessage(QString newMessage); - void addFriendMessage(QString message); - void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm")); - void addMessage(QLabel* author, QLabel* message, QLabel* date); signals: - void sendMessage(int, QString); void sendFile(int32_t friendId, QString, QString, long long); void startCall(int friendId); void startVideoCall(int friendId, bool video); @@ -78,31 +58,17 @@ public slots: private slots: void onSendTriggered(); void onAttachClicked(); - void onSliderRangeChanged(); void onCallTriggered(); void onVideoCallTriggered(); void onAnswerCallTriggered(); void onHangupCallTriggered(); void onCancelCallTriggered(); - void onChatContextMenuRequested(QPoint pos); - void onSaveLogClicked(); - void onEmoteButtonClicked(); - void onEmoteInsertRequested(QString str); private: Friend* f; - QHBoxLayout *headLayout, *mainFootLayout; - QVBoxLayout *headTextLayout, *mainLayout, *footButtonsSmall, *volMicLayout; - QGridLayout *mainChatLayout; - QLabel *avatar, *name, *statusMessage; - ChatTextEdit *msgEdit; - QPushButton *sendButton, *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton; - QScrollArea *chatArea; - QWidget *main, *head, *chatAreaWidget; - QString previousName; + QLabel *statusMessageLabel; NetCamView* netcam; - int curRow; - bool lockSliderToBottom, audioInputFlag; + bool audioInputFlag; int callId; }; diff --git a/widget/form/genericchatform.cpp b/widget/form/genericchatform.cpp new file mode 100644 index 000000000..d7727adf7 --- /dev/null +++ b/widget/form/genericchatform.cpp @@ -0,0 +1,323 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the COPYING file for more details. +*/ + +#include "genericchatform.h" +#include "ui_mainwindow.h" +#include +#include +#include +#include "smileypack.h" +#include "widget/emoticonswidget.h" +#include "style.h" +#include "widget/widget.h" + +GenericChatForm::GenericChatForm(QObject *parent) : + QObject(parent) +{ + lockSliderToBottom = true; + curRow = 0; + + mainWidget = new QWidget(); headWidget = new QWidget(); chatAreaWidget = new QWidget(); + + nameLabel = new QLabel(); + avatarLabel = new QLabel(); + QHBoxLayout *headLayout = new QHBoxLayout(), *mainFootLayout = new QHBoxLayout(); + headTextLayout = new QVBoxLayout(); + QVBoxLayout *mainLayout = new QVBoxLayout(); + QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout(); + mainChatLayout = new QGridLayout(); + + msgEdit = new ChatTextEdit(); + + sendButton = new QPushButton(); + emoteButton = new QPushButton(); + + fileButton = new QPushButton(); + callButton = new QPushButton(); + videoButton = new QPushButton(); + volButton = new QPushButton(); + micButton = new QPushButton(); + + chatArea = new QScrollArea(); + + QFont bold; + bold.setBold(true); + nameLabel->setFont(bold); + + chatAreaWidget->setLayout(mainChatLayout); + chatAreaWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css")); + + chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + chatArea->setWidgetResizable(true); + chatArea->setContextMenuPolicy(Qt::CustomContextMenu); + chatArea->setFrameStyle(QFrame::NoFrame); + + mainChatLayout->setColumnStretch(1,1); + mainChatLayout->setSpacing(5); + + footButtonsSmall->setSpacing(2); + + msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css")); + msgEdit->setFixedHeight(50); + msgEdit->setFrameStyle(QFrame::NoFrame); + + sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css")); + fileButton->setStyleSheet(Style::get(":/ui/fileButton/fileButton.css")); + emoteButton->setStyleSheet(Style::get(":/ui/emoteButton/emoteButton.css")); + + callButton->setObjectName("green"); + callButton->setStyleSheet(Style::get(":/ui/callButton/callButton.css")); + + videoButton->setObjectName("green"); + videoButton->setStyleSheet(Style::get(":/ui/videoButton/videoButton.css")); + + QString volButtonStylesheet = ""; + try + { + QFile f(":/ui/volButton/volButton.css"); + f.open(QFile::ReadOnly | QFile::Text); + QTextStream volButtonStylesheetStream(&f); + volButtonStylesheet = volButtonStylesheetStream.readAll(); + } + catch (int e) {} + + volButton->setObjectName("green"); + volButton->setStyleSheet(volButtonStylesheet); + + QString micButtonStylesheet = ""; + try + { + QFile f(":/ui/micButton/micButton.css"); + f.open(QFile::ReadOnly | QFile::Text); + QTextStream micButtonStylesheetStream(&f); + micButtonStylesheet = micButtonStylesheetStream.readAll(); + } + catch (int e) {} + + micButton->setObjectName("green"); + micButton->setStyleSheet(micButtonStylesheet); + + mainWidget->setLayout(mainLayout); + mainLayout->addWidget(chatArea); + mainLayout->addLayout(mainFootLayout); + mainLayout->setMargin(0); + + footButtonsSmall->addWidget(emoteButton); + footButtonsSmall->addWidget(fileButton); + + mainFootLayout->addWidget(msgEdit); + mainFootLayout->addLayout(footButtonsSmall); + mainFootLayout->addSpacing(5); + mainFootLayout->addWidget(sendButton); + mainFootLayout->setSpacing(0); + + headWidget->setLayout(headLayout); + headLayout->addWidget(avatarLabel); + headLayout->addLayout(headTextLayout); + headLayout->addStretch(); + headLayout->addLayout(volMicLayout); + headLayout->addWidget(callButton); + headLayout->addWidget(videoButton); + + volMicLayout->addWidget(micButton); + volMicLayout->addWidget(volButton); + + headTextLayout->addStretch(); + headTextLayout->addWidget(nameLabel); + + chatArea->setWidget(chatAreaWidget); + + //Fix for incorrect layouts on OS X as per + //https://bugreports.qt-project.org/browse/QTBUG-14591 + sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + + connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked())); + connect(chatArea, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); + connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged())); +} + +void GenericChatForm::setName(const QString &newName) +{ + nameLabel->setText(newName); + nameLabel->setToolTip(newName); // for overlength names +} + +void GenericChatForm::show(Ui::MainWindow &ui) +{ + ui.mainContent->layout()->addWidget(mainWidget); + ui.mainHead->layout()->addWidget(headWidget); + mainWidget->show(); + headWidget->show(); +} + +void GenericChatForm::onChatContextMenuRequested(QPoint pos) +{ + QWidget* sender = (QWidget*)QObject::sender(); + pos = sender->mapToGlobal(pos); + QMenu menu; + menu.addAction(tr("Save chat log"), this, SLOT(onSaveLogClicked())); + menu.exec(pos); +} + +void GenericChatForm::onSliderRangeChanged() +{ + QScrollBar* scroll = chatArea->verticalScrollBar(); + if (lockSliderToBottom) + scroll->setValue(scroll->maximum()); +} + +void GenericChatForm::onSaveLogClicked() +{ + QString path = QFileDialog::getSaveFileName(0,tr("Save chat log")); + if (path.isEmpty()) + return; + + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return; + + QString log; + QList labels = chatAreaWidget->findChildren(); + int i=0; + for (QLabel* label : labels) + { + log += label->text(); + if (i==2) + { + i=0; + log += '\n'; + } + else + { + log += '\t'; + i++; + } + } + + file.write(log.toUtf8()); + file.close(); +} + +void GenericChatForm::addMessage(QString author, QString message, QString date) +{ + QLabel *authorLabel = new QLabel(author); + QLabel *messageLabel = new QLabel(); + QLabel *dateLabel = new QLabel(date); + + QScrollBar* scroll = chatArea->verticalScrollBar(); + lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); + authorLabel->setAlignment(Qt::AlignTop | Qt::AlignRight); + dateLabel->setAlignment(Qt::AlignTop); + messageLabel->setWordWrap(true); + messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + authorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + dateLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + if (author == Widget::getInstance()->getUsername()) + { + QPalette pal; + pal.setColor(QPalette::WindowText, QColor(100,100,100)); + authorLabel->setPalette(pal); + messageLabel->setPalette(pal); + } + if (previousName.isEmpty() || previousName != author) + { + if (curRow) + { + mainChatLayout->setRowStretch(curRow, 0); + mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3); + } + previousName = author; + curRow++; + } + else if (curRow)// onSaveLogClicked expects 0 or 3 QLabel per line + authorLabel->setText(""); + + QColor greentext(61,204,61); + QString fontTemplate = "%2"; + + QString finalMessage; + QStringList messageLines = message.split("\n"); + for (QString& s : messageLines) + { + if (QRegExp("^[ ]*>.*").exactMatch(s)) + finalMessage += fontTemplate.arg(greentext.name(), toHtmlChars(s)); + else + finalMessage += toHtmlChars(s); + finalMessage += "
"; + } + messageLabel->setText(finalMessage.left(finalMessage.length()-4)); + messageLabel->setText(SmileyPack::getInstance().smileyfied(messageLabel->text())); + messageLabel->setTextFormat(Qt::RichText); + + mainChatLayout->addWidget(authorLabel, curRow, 0); + mainChatLayout->addWidget(messageLabel, curRow, 1); + mainChatLayout->addWidget(dateLabel, curRow, 3); + mainChatLayout->setRowStretch(curRow+1, 1); + mainChatLayout->setRowStretch(curRow, 0); + curRow++; + authorLabel->setContextMenuPolicy(Qt::CustomContextMenu); + messageLabel->setContextMenuPolicy(Qt::CustomContextMenu); + dateLabel->setContextMenuPolicy(Qt::CustomContextMenu); + connect(authorLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); + connect(messageLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); + connect(dateLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); +} + +GenericChatForm::~GenericChatForm() +{ + delete mainWidget; + delete headWidget; +} + +void GenericChatForm::onEmoteButtonClicked() +{ + // don't show the smiley selection widget if there are no smileys available + if (SmileyPack::getInstance().getEmoticons().empty()) + return; + + EmoticonsWidget widget; + connect(&widget, SIGNAL(insertEmoticon(QString)), this, SLOT(onEmoteInsertRequested(QString))); + + QWidget* sender = qobject_cast(QObject::sender()); + if (sender) + { + QPoint pos = -QPoint(widget.sizeHint().width() / 2, widget.sizeHint().height()) - QPoint(0, 10); + widget.exec(sender->mapToGlobal(pos)); + } +} + +void GenericChatForm::onEmoteInsertRequested(QString str) +{ + // insert the emoticon + QWidget* sender = qobject_cast(QObject::sender()); + if (sender) + msgEdit->insertPlainText(str); + + msgEdit->setFocus(); // refocus so that we can continue typing +} + +QString GenericChatForm::toHtmlChars(const QString &str) +{ + static QList> replaceList = {{"&","&"}, {" "," "}, {">",">"}, {"<","<"}}; + QString res = str; + + for (auto &it : replaceList) + res = res.replace(it.first,it.second); + + return res; +} diff --git a/widget/form/genericchatform.h b/widget/form/genericchatform.h new file mode 100644 index 000000000..59216f755 --- /dev/null +++ b/widget/form/genericchatform.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the COPYING file for more details. +*/ + +#ifndef GENERICCHATFORM_H +#define GENERICCHATFORM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "widget/tool/chattextedit.h" + +// Spacing in px inserted when the author of the last message changes +#define AUTHOR_CHANGE_SPACING 5 + +namespace Ui { + class MainWindow; +} + +class GenericChatForm : public QObject +{ + Q_OBJECT +public: + GenericChatForm(QObject *parent = 0); + virtual ~GenericChatForm(); + + virtual void setName(const QString &newName); + virtual void show(Ui::MainWindow &ui); + void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm")); + +signals: + void sendMessage(int, QString); + +public slots: + +protected slots: + void onChatContextMenuRequested(QPoint pos); + void onSliderRangeChanged(); + void onSaveLogClicked(); + void onEmoteButtonClicked(); + void onEmoteInsertRequested(QString str); + +protected: + QLabel *nameLabel, *avatarLabel; + QWidget *mainWidget, *headWidget, *chatAreaWidget; + QScrollArea *chatArea; + QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton; + QGridLayout *mainChatLayout; + QVBoxLayout *headTextLayout; + ChatTextEdit *msgEdit; + QPushButton *sendButton; + QString previousName; + int curRow; + bool lockSliderToBottom; + +private: + QString toHtmlChars(const QString &str); +}; + +#endif // GENERICCHATFORM_H diff --git a/widget/form/groupchatform.cpp b/widget/form/groupchatform.cpp index e5e630ae1..e444e0ac3 100644 --- a/widget/form/groupchatform.cpp +++ b/widget/form/groupchatform.cpp @@ -18,36 +18,27 @@ #include "group.h" #include "widget/groupwidget.h" #include "widget/widget.h" -#include "friend.h" -#include "friendlist.h" -#include "style.h" -#include -#include -#include -#include -#include -#include GroupChatForm::GroupChatForm(Group* chatGroup) - : group(chatGroup), curRow{0}, lockSliderToBottom{true} + : group(chatGroup) { - main = new QWidget(), head = new QWidget(), chatAreaWidget = new QWidget(); - headLayout = new QHBoxLayout(), mainFootLayout = new QHBoxLayout(); - headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout(); - mainChatLayout = new QGridLayout(); - avatar = new QLabel(), name = new QLabel(), nusers = new QLabel(), namesList = new QLabel(); - msgEdit = new ChatTextEdit(); - sendButton = new QPushButton(); - chatArea = new QScrollArea(); - QFont bold; - bold.setBold(true); + nusersLabel = new QLabel(); + namesList = new QLabel(); + + fileButton->setEnabled(false); + callButton->setVisible(false); + videoButton->setVisible(false); + volButton->setVisible(false); + micButton->setVisible(false); + QFont small; small.setPixelSize(10); - name->setText(group->widget->name.text()); - name->setFont(bold); - nusers->setFont(small); - nusers->setText(GroupChatForm::tr("%1 users in chat","Number of users in chat").arg(group->peers.size())); - avatar->setPixmap(QPixmap(":/img/group.png")); + + nameLabel->setText(group->widget->name.text()); + nusersLabel->setFont(small); + nusersLabel->setText(GroupChatForm::tr("%1 users in chat","Number of users in chat").arg(group->peers.size())); + avatarLabel->setPixmap(QPixmap(":/img/group_dark.png")); + QString names; for (QString& s : group->peers) names.append(s+", "); @@ -55,77 +46,24 @@ GroupChatForm::GroupChatForm(Group* chatGroup) namesList->setText(names); namesList->setFont(small); - chatAreaWidget->setLayout(mainChatLayout); - - chatArea->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css")); - chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - chatArea->setWidgetResizable(true); - chatArea->setContextMenuPolicy(Qt::CustomContextMenu); - chatArea->setFrameStyle(QFrame::NoFrame); - - mainChatLayout->setColumnStretch(1,1); - mainChatLayout->setSpacing(10); - msgEdit->setObjectName("group"); - msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css")); - msgEdit->setFixedHeight(50); - msgEdit->setFrameStyle(QFrame::NoFrame); mainChatLayout->setColumnStretch(1,1); mainChatLayout->setHorizontalSpacing(10); - sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css")); - sendButton->setFixedSize(50, 50); - - main->setLayout(mainLayout); - mainLayout->addWidget(chatArea); - mainLayout->addLayout(mainFootLayout); - mainLayout->setMargin(0); - - mainFootLayout->addWidget(msgEdit); - mainFootLayout->addWidget(sendButton); - - head->setLayout(headLayout); - headLayout->addWidget(avatar); - headLayout->addLayout(headTextLayout); - headLayout->addStretch(); - headLayout->setMargin(0); - - headTextLayout->addStretch(); - headTextLayout->addWidget(name); - headTextLayout->addWidget(nusers); + headTextLayout->addWidget(nusersLabel); headTextLayout->addWidget(namesList); headTextLayout->setMargin(0); headTextLayout->setSpacing(0); headTextLayout->addStretch(); - chatArea->setWidget(chatAreaWidget); - - sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); - connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered())); connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered())); - connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged())); - connect(chatArea, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); } GroupChatForm::~GroupChatForm() { - delete head; - delete main; -} -void GroupChatForm::show(Ui::MainWindow &ui) -{ - ui.mainContent->layout()->addWidget(main); - ui.mainHead->layout()->addWidget(head); - main->show(); - head->show(); -} - -void GroupChatForm::setName(QString newName) -{ - name->setText(newName); } void GroupChatForm::onSendTriggered() @@ -139,124 +77,21 @@ void GroupChatForm::onSendTriggered() void GroupChatForm::addGroupMessage(QString message, int peerId) { - QLabel *msgAuthor; + QString msgAuthor; if (group->peers.contains(peerId)) - msgAuthor = new QLabel(group->peers[peerId]); + msgAuthor = group->peers[peerId]; else - msgAuthor = new QLabel(tr("")); + msgAuthor = tr(""); - QLabel *msgText = new QLabel(message); - QLabel *msgDate = new QLabel(QTime::currentTime().toString("hh:mm")); - - addMessage(msgAuthor, msgText, msgDate); -} - -void GroupChatForm::addMessage(QString author, QString message, QString date) -{ - addMessage(new QLabel(author), new QLabel(message), new QLabel(date)); -} - -void GroupChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date) -{ - QPalette greentext; - greentext.setColor(QPalette::WindowText, QColor(61,204,61)); - QScrollBar* scroll = chatArea->verticalScrollBar(); - lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); - author->setAlignment(Qt::AlignTop | Qt::AlignLeft); - date->setAlignment(Qt::AlignTop); - message->setWordWrap(true); - message->setTextInteractionFlags(Qt::TextBrowserInteraction); - author->setTextInteractionFlags(Qt::TextBrowserInteraction); - date->setTextInteractionFlags(Qt::TextBrowserInteraction); - if (author->text() == Widget::getInstance()->getUsername()) - { - QPalette pal; - pal.setColor(QPalette::WindowText, Qt::gray); - author->setPalette(pal); - message->setPalette(pal); - } - if (previousName.isEmpty() || previousName != author->text()) - { - if (curRow) - { - mainChatLayout->setRowStretch(curRow, 0); - mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3); - } - previousName = author->text(); - curRow++; - } - else if (curRow)// onSaveLogClicked expects 0 or 3 QLabel per line - author->setText(""); - if (message->text()[0] == '>') - message->setPalette(greentext); - mainChatLayout->addWidget(author, curRow, 0); - mainChatLayout->addWidget(message, curRow, 1); - mainChatLayout->addWidget(date, curRow, 3); - mainChatLayout->setRowStretch(curRow+1, 1); - mainChatLayout->setRowStretch(curRow, 0); - curRow++; - author->setContextMenuPolicy(Qt::CustomContextMenu); - message->setContextMenuPolicy(Qt::CustomContextMenu); - date->setContextMenuPolicy(Qt::CustomContextMenu); - connect(author, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); - connect(message, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); - connect(date, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); -} - -void GroupChatForm::onSliderRangeChanged() -{ - QScrollBar* scroll = chatArea->verticalScrollBar(); - if (lockSliderToBottom) - scroll->setValue(scroll->maximum()); + addMessage(msgAuthor, message); } void GroupChatForm::onUserListChanged() { - nusers->setText(tr("%1 users in chat").arg(group->nPeers)); + nusersLabel->setText(tr("%1 users in chat").arg(group->nPeers)); QString names; for (QString& s : group->peers) names.append(s+", "); names.chop(2); namesList->setText(names); } - -void GroupChatForm::onChatContextMenuRequested(QPoint pos) -{ - QWidget* sender = (QWidget*)QObject::sender(); - pos = sender->mapToGlobal(pos); - QMenu menu; - menu.addAction("Save chat log", this, SLOT(onSaveLogClicked())); - menu.exec(pos); -} - -void GroupChatForm::onSaveLogClicked() -{ - QString path = QFileDialog::getSaveFileName(0,tr("Save chat log")); - if (path.isEmpty()) - return; - - QFile file(path); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) - return; - - QString log; - QList labels = chatAreaWidget->findChildren(); - int i=0; - for (QLabel* label : labels) - { - log += label->text(); - if (i==2) - { - i=0; - log += '\n'; - } - else - { - log += '\t'; - i++; - } - } - - file.write(log.toUtf8()); - file.close(); -} diff --git a/widget/form/groupchatform.h b/widget/form/groupchatform.h index af008476a..9b4766180 100644 --- a/widget/form/groupchatform.h +++ b/widget/form/groupchatform.h @@ -17,58 +17,27 @@ #ifndef GROUPCHATFORM_H #define GROUPCHATFORM_H -#include -#include -#include -#include -#include -#include -#include -#include - +#include "genericchatform.h" #include "widget/tool/chattextedit.h" #include "ui_mainwindow.h" -// Spacing in px inserted when the author of the last message changes -#define AUTHOR_CHANGE_SPACING 5 - class Group; -class GroupChatForm : public QObject +class GroupChatForm : public GenericChatForm { Q_OBJECT public: GroupChatForm(Group* chatGroup); ~GroupChatForm(); - void show(Ui::MainWindow &ui); - void setName(QString newName); void addGroupMessage(QString message, int peerId); - void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm")); - void addMessage(QLabel* author, QLabel* message, QLabel* date); void onUserListChanged(); -signals: - void sendMessage(int, QString); - private slots: void onSendTriggered(); - void onSliderRangeChanged(); - void onChatContextMenuRequested(QPoint pos); - void onSaveLogClicked(); private: Group* group; - QHBoxLayout *headLayout, *mainFootLayout; - QVBoxLayout *headTextLayout, *mainLayout; - QGridLayout *mainChatLayout; - QLabel *avatar, *name, *nusers, *namesList; - ChatTextEdit *msgEdit; - QPushButton *sendButton; - QScrollArea *chatArea; - QWidget *main, *head, *chatAreaWidget; - QString previousName; - int curRow; - bool lockSliderToBottom; + QLabel *nusersLabel, *namesList; }; #endif // GROUPCHATFORM_H diff --git a/widget/widget.cpp b/widget/widget.cpp index 81cd893bf..d08ea0fe1 100644 --- a/widget/widget.cpp +++ b/widget/widget.cpp @@ -509,7 +509,7 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message) if (!f) return; - f->chatForm->addFriendMessage(message); + f->chatForm->addMessage(f->getName(), message); if (activeChatroomWidget != nullptr) {