diff --git a/filetransferinstance.cpp b/filetransferinstance.cpp new file mode 100644 index 000000000..e927545c6 --- /dev/null +++ b/filetransferinstance.cpp @@ -0,0 +1,329 @@ +/* + 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 "filetransferinstance.h" +#include "widget/widget.h" +#include "core.h" +#include "math.h" +#include "style.h" +#include +#include +#include +#include +#include + +uint FileTransferInstance::Idconter = 0; + +FileTransferInstance::FileTransferInstance(ToxFile File) + : lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0}, + fileNum{File.fileNum}, friendId{File.friendId}, direction{File.direction} +{ + id = Idconter++; + state = tsPending; + + filename = File.fileName; + size = getHumanReadableSize(File.filesize); + speed = "0B/s"; + eta = "00:00"; + if (File.direction == ToxFile::SENDING) + { + QImage preview; + File.file->seek(0); + if (preview.loadFromData(File.file->readAll())) + { + pic = preview.scaledToHeight(50); + } + File.file->seek(0); + } +} + +QString FileTransferInstance::getHumanReadableSize(unsigned long long size) +{ + static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"}; + int exp = 0; + if (size) + exp = std::min( (int) (log(size) / log(1024)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1)); + return QString().setNum(size / pow(1024, exp),'f',2).append(suffix[exp]); +} + +void FileTransferInstance::onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction) +{ + if (FileNum != fileNum || FriendId != friendId || Direction != direction) + return; + + state = tsProcessing; + QDateTime newtime = QDateTime::currentDateTime(); + int timediff = lastUpdate.secsTo(newtime); + if (timediff <= 0) + return; + qint64 diff = BytesSent - lastBytesSent; + if (diff < 0) + { + qWarning() << "FileTransferInstance::onFileTransferInfo: Negative transfer speed !"; + diff = 0; + } + long rawspeed = diff / timediff; + speed = getHumanReadableSize(rawspeed)+"/s"; + size = getHumanReadableSize(Filesize); + if (!rawspeed) + return; + int etaSecs = (Filesize - BytesSent) / rawspeed; + QTime etaTime(0,0); + etaTime = etaTime.addSecs(etaSecs); + eta = etaTime.toString("mm:ss"); + lastUpdate = newtime; + lastBytesSent = BytesSent; + emit stateUpdated(); +} + +void FileTransferInstance::onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction) +{ + if (FileNum != fileNum || FriendId != friendId || Direction != direction) + return; + disconnect(Widget::getInstance()->getCore(),0,this,0); + state = tsCanceled; + + emit stateUpdated(); +} + +void FileTransferInstance::onFileTransferFinished(ToxFile File) +{ + if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction) + return; + disconnect(Widget::getInstance()->getCore(),0,this,0); + + if (File.direction == ToxFile::RECEIVING) + { + QImage preview; + QFile previewFile(File.filePath); + if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= 1024*1024*25) // Don't preview big (>25MiB) images + { + if (preview.loadFromData(previewFile.readAll())) + { + pic = preview.scaledToHeight(50); + } + previewFile.close(); + } + } + + state = tsFinished; + + emit stateUpdated(); +} + +void FileTransferInstance::cancelTransfer() +{ + Widget::getInstance()->getCore()->cancelFileSend(friendId, fileNum); + state = tsCanceled; + emit stateUpdated(); +} + +void FileTransferInstance::rejectRecvRequest() +{ + Widget::getInstance()->getCore()->rejectFileRecvRequest(friendId, fileNum); + onFileTransferCancelled(friendId, fileNum, direction); + state = tsCanceled; + emit stateUpdated(); +} + +// for whatever the fuck reason, QFileInfo::isWritable() always fails for files that don't exist +// which makes it useless for our case +// since QDir doesn't have an isWritable(), the only option I can think of is to make/delete the file +// surely this is a common problem that has a qt-implemented solution? +bool isFileWritable(QString& path) +{ + QFile file(path); + bool exists = file.exists(); + bool out = file.open(QIODevice::WriteOnly); + file.close(); + if (!exists) + file.remove(); + return out; +} + +void FileTransferInstance::acceptRecvRequest() +{ + QString path; + while (true) + { + path = QFileDialog::getSaveFileName(Widget::getInstance(), tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename)); + if (path.isEmpty()) + return; + else + { + //bool savable = QFileInfo(path).isWritable(); + //qDebug() << path << " is writable: " << savable; + //qDebug() << "/home/bill/bliss.pdf writable: " << QFileInfo("/home/bill/bliss.pdf").isWritable(); + if (isFileWritable(path)) + break; + else + QMessageBox::warning(Widget::getInstance(), tr("Location not writable","Title of permissions popup"), tr("You do not have permission to write that location. Choose another, or cancel the save dialog.", "text of permissions popup")); + } + } + + savePath = path; + + Widget::getInstance()->getCore()->acceptFileRecvRequest(friendId, fileNum, path); + state = tsProcessing; + + emit stateUpdated(); +} + +void FileTransferInstance::pauseResumeRecv() +{ + Widget::getInstance()->getCore()->pauseResumeFileRecv(friendId, fileNum); + if (state == tsProcessing) + state = tsPaused; + else state = tsProcessing; + emit stateUpdated(); +} + +void FileTransferInstance::pauseResumeSend() +{ + Widget::getInstance()->getCore()->pauseResumeFileSend(friendId, fileNum); + if (state == tsProcessing) + state = tsPaused; + else state = tsProcessing; + emit stateUpdated(); +} + +QString FileTransferInstance::QImage2base64(const QImage &img) +{ + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + img.save(&buffer, "PNG"); // writes image into ba in PNG format + return ba.toBase64(); +} + +QString FileTransferInstance::getHtmlImage() +{ + qDebug() << "QString FileTransferInstance::getHtmlImage() " << state; + + QString res; + if (state == tsPending || state == tsProcessing || state == tsPaused) + { + QImage leftBtnImg(":/ui/fileTransferInstance/stopFileButton.png"); + QImage rightBtnImg; + if (state == tsProcessing) + rightBtnImg = QImage(":/ui/fileTransferInstance/pauseFileButton.png"); + else if (state == tsPaused) + rightBtnImg = QImage(":/ui/fileTransferInstance/resumeFileButton.png"); + else + rightBtnImg = QImage(":/ui/fileTransferInstance/acceptFileButton.png"); + + res = draw2ButtonsForm("silver", leftBtnImg, rightBtnImg); + } else if (state == tsCanceled) + { + res = drawButtonlessForm("red"); + } else if (state == tsFinished) + { + res = drawButtonlessForm("green"); + } + + return res; +} + +void FileTransferInstance::pressFromHtml(QString code) +{ + if (state == tsFinished || state == tsCanceled) + return; + + if (direction == ToxFile::SENDING) + { + if (code == "btnA") + cancelTransfer(); + else if (code == "btnB") + pauseResumeSend(); + } else { + if (code == "btnA") + rejectRecvRequest(); + else if (code == "btnB") + { + if (state == tsPending) + acceptRecvRequest(); + else + pauseResumeRecv(); + } + } +} + +QString FileTransferInstance::drawButtonlessForm(const QString &type) +{ + QString imgAStr; + QString imgBStr; + + if (type == "red") + { + imgAStr = ""; + imgBStr = ""; + } else { + imgAStr = ""; + imgBStr = ""; + } + + QString content = "

" + filename + "

" + size + "

"; + + return wrapIntoForm(content, type, imgAStr, imgBStr); +} + +QString FileTransferInstance::insertMiniature(const QString &type) +{ + if (pic == QImage()) + return QString(); + + QString widgetId = QString::number(getId()); + + QString res; + res = "
\n"; + res += ""; + res += "
\n"; + return res; +} + +QString FileTransferInstance::draw2ButtonsForm(const QString &type, const QImage &imgA, const QImage &imgB) +{ + QString widgetId = QString::number(getId()); + QString imgAstr = ""; + QString imgBstr = ""; + + QString content; + content += "

" + filename + "

"; + content += "

" + getHumanReadableSize(lastBytesSent) + " / " + size + " (" + speed + " ETA: " + eta + ")

\n"; + + return wrapIntoForm(content, type, imgAstr, imgBstr); +} + +QString FileTransferInstance::wrapIntoForm(const QString& content, const QString &type, const QString &imgAstr, const QString &imgBstr) +{ + QString res; + + res = "\n"; + res += "\n"; + res += insertMiniature(type); + res += "\n"; + res += "\n"; + res += "\n"; + res += "
\n"; + res += "
"; + res += content; + res += "
\n"; + res += "
\n"; + res += "
" + imgAstr + "
" + imgBstr+ "
\n"; + res += "
\n"; + + return res; +} diff --git a/widget/filetransfertwidget.h b/filetransferinstance.h similarity index 64% rename from widget/filetransfertwidget.h rename to filetransferinstance.h index 699c9d548..ff5d85937 100644 --- a/widget/filetransfertwidget.h +++ b/filetransferinstance.h @@ -13,11 +13,10 @@ See the COPYING file for more details. */ +#ifndef FILETRANSFERINSTANCE_H +#define FILETRANSFERINSTANCE_H -#ifndef FILETRANSFERTWIDGET_H -#define FILETRANSFERTWIDGET_H - -#include +#include #include #include #include @@ -29,17 +28,22 @@ struct ToxFile; -class FileTransfertWidget : public QWidget +class FileTransferInstance : public QObject { Q_OBJECT - public: - FileTransfertWidget(ToxFile File); + explicit FileTransferInstance(ToxFile File); + QString getHtmlImage(); + uint getId(){return id;} public slots: void onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction); void onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction); void onFileTransferFinished(ToxFile File); + void pressFromHtml(QString); + +signals: + void stateUpdated(); private slots: void cancelTransfer(); @@ -50,14 +54,21 @@ private slots: private: QString getHumanReadableSize(unsigned long long size); + QString QImage2base64(const QImage &img); + QString drawButtonlessForm(const QString &type); + QString draw2ButtonsForm(const QString &type, const QImage &imgA, const QImage &imgB); + QString insertMiniature(const QString &type); + QString wrapIntoForm(const QString &content, const QString &type, const QString &imgAstr, const QString &imgBstr); private: - QLabel *pic, *filename, *size, *speed, *eta; - QPushButton *topright, *bottomright; - QProgressBar *progress; - QHBoxLayout *mainLayout, *textLayout; - QVBoxLayout *infoLayout, *buttonLayout; - QWidget* buttonWidget; + enum TransfState {tsPending, tsProcessing, tsPaused, tsFinished, tsCanceled}; + + static uint Idconter; + uint id; + + TransfState state; + QImage pic; + QString filename, size, speed, eta; QDateTime lastUpdate; long long lastBytesSent; int fileNum; @@ -65,7 +76,6 @@ private: QString savePath; ToxFile::FileDirection direction; QString stopFileButtonStylesheet, pauseFileButtonStylesheet, acceptFileButtonStylesheet; - void paintEvent(QPaintEvent *); }; -#endif // FILETRANSFERTWIDGET_H +#endif // FILETRANSFERINSTANCE_H diff --git a/qtox.pro b/qtox.pro index 679e6d17e..05f1c16ce 100644 --- a/qtox.pro +++ b/qtox.pro @@ -57,7 +57,7 @@ win32 { LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui } - contains(JENKINS, YES) { + contains(JENKINS, YES) { LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a -lopencv_core -lopencv_highgui -lopenal } } @@ -80,7 +80,6 @@ HEADERS += widget/form/addfriendform.h \ widget/form/filesform.h \ widget/tool/chattextedit.h \ widget/tool/friendrequestdialog.h \ - widget/filetransfertwidget.h \ widget/friendwidget.h \ widget/groupwidget.h \ widget/widget.h \ @@ -101,7 +100,11 @@ HEADERS += widget/form/addfriendform.h \ widget/adjustingscrollarea.h \ widget/croppinglabel.h \ widget/friendlistwidget.h \ - widget/genericchatroomwidget.h + widget/genericchatroomwidget.h \ + widget/form/genericchatform.h \ + widget/tool/chataction.h \ + widget/chatareawidget.h \ + filetransferinstance.h SOURCES += \ widget/form/addfriendform.cpp \ @@ -111,7 +114,6 @@ SOURCES += \ widget/form/filesform.cpp \ widget/tool/chattextedit.cpp \ widget/tool/friendrequestdialog.cpp \ - widget/filetransfertwidget.cpp \ widget/friendwidget.cpp \ widget/groupwidget.cpp \ widget/widget.cpp \ @@ -134,4 +136,8 @@ SOURCES += \ widget/croppinglabel.cpp \ widget/friendlistwidget.cpp \ coreav.cpp \ - widget/genericchatroomwidget.cpp + widget/genericchatroomwidget.cpp \ + widget/form/genericchatform.cpp \ + widget/tool/chataction.cpp \ + widget/chatareawidget.cpp \ + filetransferinstance.cpp diff --git a/res.qrc b/res.qrc index f413e0209..508fbef28 100644 --- a/res.qrc +++ b/res.qrc @@ -42,6 +42,7 @@ ui/callButton/callButtonYellowHover.png ui/callButton/callButtonYellowPressed.png ui/chatArea/chatArea.css + ui/chatArea/innerStyle.css ui/chatArea/scrollBarDownArrow.png ui/chatArea/scrollBarDownArrowHover.png ui/chatArea/scrollBarDownArrowPressed.png @@ -57,6 +58,7 @@ ui/fileButton/fileButton.png ui/fileButton/fileButtonHover.png ui/fileButton/fileButtonPressed.png + ui/fileButton/fileButtonDisabled.png ui/msgEdit/msgEdit.css ui/pauseFileButton/default.png ui/pauseFileButton/hover.png @@ -124,5 +126,13 @@ ui/micButton/micButtonPressed.png ui/micButton/micButton.css ui/volButton/volButton.css + ui/fileTransferInstance/acceptFileButton.png + ui/fileTransferInstance/pauseFileButton.png + ui/fileTransferInstance/resumeFileButton.png + ui/fileTransferInstance/stopFileButton.png + ui/fileTransferInstance/emptyLGreenFileButton.png + ui/fileTransferInstance/emptyLRedFileButton.png + ui/fileTransferInstance/emptyRGreenFileButton.png + ui/fileTransferInstance/emptyRRedFileButton.png diff --git a/translations/it.qm b/translations/it.qm index 4fdfacd63..890aaaea2 100644 Binary files a/translations/it.qm and b/translations/it.qm differ diff --git a/translations/it.ts b/translations/it.ts index af860d4d8..65a34eb9f 100644 --- a/translations/it.ts +++ b/translations/it.ts @@ -78,33 +78,27 @@ ChatForm - + Send a file Invia un file - - - - Save chat log - Salva il log della chat - - FileTransfertWidget + FileTransferInstance - + Save a file Title of the file saving dialog Salva file - + Location not writable Title of permissions popup Errore - + You do not have permission to write that location. Choose another, or cancel the save dialog. text of permissions popup Non hai sufficienti permessi per scrivere in questa locazione. Scegli un'altra posizione, o annulla il salvataggio. @@ -186,29 +180,33 @@ Rimuovi contatto + + GenericChatForm + + + + Save chat log + Salva il log della chat + + GroupChatForm - + %1 users in chat Number of users in chat %1 utenti in chat - + <Unknown> <Sconosciuto> - + %1 users in chat %1 utenti in chat - - - Save chat log - Salva il log della chat - GroupWidget diff --git a/ui/chatArea/innerStyle.css b/ui/chatArea/innerStyle.css new file mode 100644 index 000000000..5a1de4a94 --- /dev/null +++ b/ui/chatArea/innerStyle.css @@ -0,0 +1,63 @@ +div.name_me { + color: #646464; + font-weight: bold; + padding-right: 3px; +} + +div.name { + color: #000000; + font-weight: bold; + padding-right: 3px; +} + +div.message { + color: #000000; + padding-right: 3px; + padding-left: 3px; +} + +div.date { + color: #000000; + padding-left: 3px; +} + +div.quote { + background-color: #6bc260; +} + +div.green { + margin-top: 12px; + margin-bottom: 12px; + margin-left: 12px; + margin-right: 12px; + color: #ffffff; + background-color: #6bc260; + font-size: 10px; +} + +div.silver { + margin-top: 12px; + margin-bottom: 12px; + margin-left: 12px; + margin-right: 12px; + color: #000000; + background-color: #d1d1d1; + font-size: 10px; +} + +div.red { + margin-top: 12px; + margin-bottom: 12px; + margin-left: 12px; + margin-right: 12px; + color: #ffffff; + background-color: rgb(200,78,78); + font-size: 10px; +} + +div.button { + margin-top: 0px; + margin-bottom: 0px; + margin-left: 0px; + color: #ffffff; +} 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/ui/fileTransferInstance/acceptFileButton.png b/ui/fileTransferInstance/acceptFileButton.png new file mode 100644 index 000000000..b072344ed Binary files /dev/null and b/ui/fileTransferInstance/acceptFileButton.png differ diff --git a/ui/fileTransferInstance/emptyLGreenFileButton.png b/ui/fileTransferInstance/emptyLGreenFileButton.png new file mode 100644 index 000000000..f15523ddf Binary files /dev/null and b/ui/fileTransferInstance/emptyLGreenFileButton.png differ diff --git a/ui/fileTransferInstance/emptyLRedFileButton.png b/ui/fileTransferInstance/emptyLRedFileButton.png new file mode 100644 index 000000000..37e102da2 Binary files /dev/null and b/ui/fileTransferInstance/emptyLRedFileButton.png differ diff --git a/ui/fileTransferInstance/emptyRGreenFileButton.png b/ui/fileTransferInstance/emptyRGreenFileButton.png new file mode 100644 index 000000000..b95d87ae2 Binary files /dev/null and b/ui/fileTransferInstance/emptyRGreenFileButton.png differ diff --git a/ui/fileTransferInstance/emptyRRedFileButton.png b/ui/fileTransferInstance/emptyRRedFileButton.png new file mode 100644 index 000000000..4d1236066 Binary files /dev/null and b/ui/fileTransferInstance/emptyRRedFileButton.png differ diff --git a/ui/fileTransferInstance/pauseFileButton.png b/ui/fileTransferInstance/pauseFileButton.png new file mode 100644 index 000000000..adeaa238c Binary files /dev/null and b/ui/fileTransferInstance/pauseFileButton.png differ diff --git a/ui/fileTransferInstance/resumeFileButton.png b/ui/fileTransferInstance/resumeFileButton.png new file mode 100644 index 000000000..4bf1efd12 Binary files /dev/null and b/ui/fileTransferInstance/resumeFileButton.png differ diff --git a/ui/fileTransferInstance/stopFileButton.png b/ui/fileTransferInstance/stopFileButton.png new file mode 100644 index 000000000..36525ad3a Binary files /dev/null and b/ui/fileTransferInstance/stopFileButton.png differ diff --git a/widget/chatareawidget.cpp b/widget/chatareawidget.cpp new file mode 100644 index 000000000..e3d021dd7 --- /dev/null +++ b/widget/chatareawidget.cpp @@ -0,0 +1,105 @@ +/* + 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 "chatareawidget.h" +#include +#include +#include + +ChatAreaWidget::ChatAreaWidget(QWidget *parent) : + QTextEdit(parent) +{ + setReadOnly(true); + viewport()->setCursor(Qt::ArrowCursor); + setContextMenuPolicy(Qt::CustomContextMenu); +} + +ChatAreaWidget::~ChatAreaWidget() +{ + for (ChatAction *it : messages) + delete it; +} + +void ChatAreaWidget::mouseReleaseEvent(QMouseEvent * event) +{ + QTextEdit::mouseReleaseEvent(event); + int pos = this->document()->documentLayout()->hitTest(event->pos(), Qt::ExactHit); + if (pos > 0) + { + QTextCursor cursor(document()); + cursor.setPosition(pos); + if( ! cursor.atEnd() ) + { + cursor.setPosition(pos+1); + + QTextFormat format = cursor.charFormat(); + if (format.isImageFormat()) + { + QString imageName = format.toImageFormat().name(); + if (QRegExp("^data:ftrans.*").exactMatch(imageName)) + { + QString data = imageName.right(imageName.length() - 12); + int endpos = data.indexOf("/png;base64"); + data = data.left(endpos); + int middlepos = data.indexOf("."); + QString widgetID = data.left(middlepos); + QString widgetBtn = data.right(data.length() - middlepos - 1); + emit onFileTranfertInterract(widgetID, widgetBtn); + } + } + } + } +} + +QString ChatAreaWidget::getHtmledMessages() +{ + QString res("\n"); + + for (ChatAction *it : messages) + { + res += it->getHtml(); + } + res += "
"; + return res; +} + +void ChatAreaWidget::insertMessage(ChatAction *msgAction) +{ + if (msgAction == nullptr) + return; + + messages.append(msgAction); + updateChatContent(); +} + +void ChatAreaWidget::updateChatContent() +{ + QScrollBar* scroll = verticalScrollBar(); + lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); + + setHtml(getHtmledMessages()); + if (lockSliderToBottom) + sliderPosition = scroll->maximum(); + + scroll->setValue(sliderPosition); +} + +void ChatAreaWidget::clearMessages() +{ + for (ChatAction *it : messages) + delete it; + updateChatContent(); +} diff --git a/widget/chatareawidget.h b/widget/chatareawidget.h new file mode 100644 index 000000000..2d85fc453 --- /dev/null +++ b/widget/chatareawidget.h @@ -0,0 +1,49 @@ +/* + 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 CHATAREAWIDGET_H +#define CHATAREAWIDGET_H + +#include +#include +#include "widget/tool/chataction.h" + +class ChatAreaWidget : public QTextEdit +{ + Q_OBJECT +public: + explicit ChatAreaWidget(QWidget *parent = 0); + virtual ~ChatAreaWidget(); + void insertMessage(ChatAction *msgAction); + void clearMessages(); + +signals: + void onFileTranfertInterract(QString widgetName, QString buttonName); + +protected: + void mouseReleaseEvent(QMouseEvent * event); + +public slots: + void updateChatContent(); + +private: + QString getHtmledMessages(); + QList messages; + bool lockSliderToBottom; + int sliderPosition; +}; + +#endif // CHATAREAWIDGET_H diff --git a/widget/filetransfertwidget.cpp b/widget/filetransfertwidget.cpp deleted file mode 100644 index 41fb4ed56..000000000 --- a/widget/filetransfertwidget.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* - 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 "filetransfertwidget.h" -#include "widget.h" -#include "core.h" -#include "math.h" -#include "style.h" -#include -#include -#include -#include - -FileTransfertWidget::FileTransfertWidget(ToxFile File) - : lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0}, - fileNum{File.fileNum}, friendId{File.friendId}, direction{File.direction} -{ - pic=new QLabel(), filename=new QLabel(), size=new QLabel(), speed=new QLabel(), eta=new QLabel(); - topright = new QPushButton(), bottomright = new QPushButton(); - progress = new QProgressBar(); - mainLayout = new QHBoxLayout(), textLayout = new QHBoxLayout(); - infoLayout = new QVBoxLayout(), buttonLayout = new QVBoxLayout(); - buttonWidget = new QWidget(); - QFont prettysmall; - prettysmall.setPixelSize(10); - this->setObjectName("default"); - this->setStyleSheet(Style::get(":/ui/fileTransferWidget/fileTransferWidget.css")); - QPalette greybg; - greybg.setColor(QPalette::Window, QColor(209,209,209)); - greybg.setColor(QPalette::Base, QColor(150,150,150)); - setPalette(greybg); - setAutoFillBackground(true); - - setMinimumSize(250,58); - setMaximumHeight(58); - setLayout(mainLayout); - mainLayout->setMargin(0); - - pic->setMaximumHeight(40); - pic->setContentsMargins(5,0,0,0); - filename->setText(File.fileName); - filename->setFont(prettysmall); - size->setText(getHumanReadableSize(File.filesize)); - size->setFont(prettysmall); - speed->setText("0B/s"); - speed->setFont(prettysmall); - eta->setText("00:00"); - eta->setFont(prettysmall); - progress->setValue(0); - progress->setMinimumHeight(11); - progress->setFont(prettysmall); - progress->setTextVisible(false); - QPalette whitebg; - whitebg.setColor(QPalette::Window, QColor(255,255,255)); - buttonWidget->setPalette(whitebg); - buttonWidget->setAutoFillBackground(true); - buttonWidget->setLayout(buttonLayout); - - stopFileButtonStylesheet = Style::get(":/ui/stopFileButton/style.css"); - pauseFileButtonStylesheet = Style::get(":/ui/pauseFileButton/style.css"); - acceptFileButtonStylesheet = Style::get(":/ui/acceptFileButton/style.css"); - - topright->setStyleSheet(stopFileButtonStylesheet); - if (File.direction == ToxFile::SENDING) - { - bottomright->setStyleSheet(pauseFileButtonStylesheet); - connect(topright, SIGNAL(clicked()), this, SLOT(cancelTransfer())); - connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeSend())); - - QPixmap preview; - File.file->seek(0); - if (preview.loadFromData(File.file->readAll())) - { - preview = preview.scaledToHeight(40); - pic->setPixmap(preview); - } - File.file->seek(0); - } - else - { - bottomright->setStyleSheet(acceptFileButtonStylesheet); - connect(topright, SIGNAL(clicked()), this, SLOT(rejectRecvRequest())); - connect(bottomright, SIGNAL(clicked()), this, SLOT(acceptRecvRequest())); - } - - QPalette toxgreen; - toxgreen.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green - topright->setIconSize(QSize(10,10)); - topright->setMinimumSize(25,28); - topright->setFlat(true); - topright->setAutoFillBackground(true); - topright->setPalette(toxgreen); - bottomright->setIconSize(QSize(10,10)); - bottomright->setMinimumSize(25,28); - bottomright->setFlat(true); - bottomright->setAutoFillBackground(true); - bottomright->setPalette(toxgreen); - - mainLayout->addStretch(); - mainLayout->addWidget(pic); - mainLayout->addLayout(infoLayout,3); - mainLayout->addStretch(); - mainLayout->addWidget(buttonWidget); - mainLayout->setMargin(0); - mainLayout->setSpacing(0); - - infoLayout->addWidget(filename); - infoLayout->addLayout(textLayout); - infoLayout->addWidget(progress); - infoLayout->setMargin(4); - infoLayout->setSpacing(4); - - textLayout->addWidget(size); - textLayout->addStretch(0); - textLayout->addWidget(speed); - textLayout->addStretch(0); - textLayout->addWidget(eta); - textLayout->setMargin(2); - textLayout->setSpacing(5); - - buttonLayout->addWidget(topright); - buttonLayout->addSpacing(2); - buttonLayout->addWidget(bottomright); - buttonLayout->setContentsMargins(2,0,0,0); - buttonLayout->setSpacing(0); -} - -QString FileTransfertWidget::getHumanReadableSize(unsigned long long size) -{ - static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"}; - int exp = 0; - if (size) - exp = std::min( (int) (log(size) / log(1024)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1)); - return QString().setNum(size / pow(1024, exp),'f',2).append(suffix[exp]); -} - -void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction) -{ - if (FileNum != fileNum || FriendId != friendId || Direction != direction) - return; - QDateTime newtime = QDateTime::currentDateTime(); - int timediff = lastUpdate.secsTo(newtime); - if (timediff <= 0) - return; - qint64 diff = BytesSent - lastBytesSent; - if (diff < 0) - { - qWarning() << "FileTransfertWidget::onFileTransferInfo: Negative transfer speed !"; - diff = 0; - } - long rawspeed = diff / timediff; - speed->setText(getHumanReadableSize(rawspeed)+"/s"); - size->setText(getHumanReadableSize(Filesize)); - if (!rawspeed) - return; - int etaSecs = (Filesize - BytesSent) / rawspeed; - QTime etaTime(0,0); - etaTime = etaTime.addSecs(etaSecs); - eta->setText(etaTime.toString("mm:ss")); - if (!Filesize) - { - progress->setValue(0); - qDebug() << QString("FT: received %1 bytes of an empty file, stop sending sequential devices, zetok!").arg(BytesSent); - } - else - { - progress->setValue(BytesSent*100/Filesize); - qDebug() << QString("FT: received %1/%2 bytes, progress is %3%").arg(BytesSent).arg(Filesize).arg(BytesSent*100/Filesize); - } - lastUpdate = newtime; - lastBytesSent = BytesSent; -} - -void FileTransfertWidget::onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction) -{ - if (FileNum != fileNum || FriendId != friendId || Direction != direction) - return; - buttonLayout->setContentsMargins(0,0,0,0); - disconnect(topright); - disconnect(Widget::getInstance()->getCore(),0,this,0); - progress->hide(); - speed->hide(); - eta->hide(); - topright->hide(); - bottomright->hide(); - QPalette whiteText; - whiteText.setColor(QPalette::WindowText, Qt::white); - filename->setPalette(whiteText); - size->setPalette(whiteText); - this->setObjectName("error"); - this->style()->polish(this); - - //Toggle window visibility to fix draw order bug - this->hide(); - this->show(); -} - -void FileTransfertWidget::onFileTransferFinished(ToxFile File) -{ - if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction) - return; - topright->disconnect(); - disconnect(Widget::getInstance()->getCore(),0,this,0); - progress->hide(); - speed->hide(); - eta->hide(); - topright->hide(); - bottomright->hide(); - buttonLayout->setContentsMargins(0,0,0,0); - QPalette whiteText; - whiteText.setColor(QPalette::WindowText, Qt::white); - filename->setPalette(whiteText); - size->setPalette(whiteText); - this->setObjectName("success"); - this->style()->polish(this); - - //Toggle window visibility to fix draw order bug - this->hide(); - this->show(); - - if (File.direction == ToxFile::RECEIVING) - { - QPixmap preview; - QFile previewFile(File.filePath); - if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= 1024*1024*25) // Don't preview big (>25MiB) images - { - if (preview.loadFromData(previewFile.readAll())) - { - preview = preview.scaledToHeight(40); - pic->setPixmap(preview); - } - previewFile.close(); - } - } -} - -void FileTransfertWidget::cancelTransfer() -{ - Widget::getInstance()->getCore()->cancelFileSend(friendId, fileNum); -} - -void FileTransfertWidget::rejectRecvRequest() -{ - Widget::getInstance()->getCore()->rejectFileRecvRequest(friendId, fileNum); - onFileTransferCancelled(friendId, fileNum, direction); -} - -// for whatever the fuck reason, QFileInfo::isWritable() always fails for files that don't exist -// which makes it useless for our case -// since QDir doesn't have an isWritable(), the only option I can think of is to make/delete the file -// surely this is a common problem that has a qt-implemented solution? -bool isWritable(QString& path) -{ - QFile file(path); - bool exists = file.exists(); - bool out = file.open(QIODevice::WriteOnly); - file.close(); - if (!exists) - file.remove(); - return out; -} - -void FileTransfertWidget::acceptRecvRequest() -{ - QString path; - while (true) - { - path = QFileDialog::getSaveFileName(this, tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename->text())); - if (path.isEmpty()) - return; - else - { - //bool savable = QFileInfo(path).isWritable(); - //qDebug() << path << " is writable: " << savable; - //qDebug() << "/home/bill/bliss.pdf writable: " << QFileInfo("/home/bill/bliss.pdf").isWritable(); - if (isWritable(path)) - break; - else - QMessageBox::warning(this, tr("Location not writable","Title of permissions popup"), tr("You do not have permission to write that location. Choose another, or cancel the save dialog.", "text of permissions popup")); - } - } - - savePath = path; - - bottomright->setStyleSheet(pauseFileButtonStylesheet); - bottomright->disconnect(); - connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeRecv())); - Widget::getInstance()->getCore()->acceptFileRecvRequest(friendId, fileNum, path); -} - -void FileTransfertWidget::pauseResumeRecv() -{ - Widget::getInstance()->getCore()->pauseResumeFileRecv(friendId, fileNum); -} - -void FileTransfertWidget::pauseResumeSend() -{ - Widget::getInstance()->getCore()->pauseResumeFileSend(friendId, fileNum); -} - -void FileTransfertWidget::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} diff --git a/widget/form/chatform.cpp b/widget/form/chatform.cpp index 50f09f5a2..dc9e3f410 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 "filetransferinstance.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,19 @@ 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())); + connect(chatWidget, SIGNAL(onFileTranfertInterract(QString,QString)), this, SLOT(onFileTansBtnClicked(QString,QString))); } 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 +68,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,48 +90,25 @@ 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) return; - QLabel *author = new QLabel(Widget::getInstance()->getUsername()); - QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm")); - QScrollBar* scroll = chatArea->verticalScrollBar(); - lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); - author->setAlignment(Qt::AlignTop | Qt::AlignRight); - date->setAlignment(Qt::AlignTop); - QPalette pal; - pal.setColor(QPalette::WindowText, Qt::gray); - author->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); - curRow++; - } - mainChatLayout->addWidget(author, curRow, 0); - } - FileTransfertWidget* fileTrans = new FileTransfertWidget(file); - previousName = author->text(); - mainChatLayout->addWidget(fileTrans, curRow, 1); - mainChatLayout->addWidget(date, curRow, 3); - mainChatLayout->setRowStretch(curRow+1, 1); - mainChatLayout->setRowStretch(curRow, 0); - curRow++; + FileTransferInstance* fileTrans = new FileTransferInstance(file); + ftransWidgets.insert(fileTrans->getId(), fileTrans); - connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransfertWidget::onFileTransferInfo); - connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled); - connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished); + connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransferInstance::onFileTransferInfo); + connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransferInstance::onFileTransferCancelled); + connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransferInstance::onFileTransferFinished); + connect(fileTrans, SIGNAL(stateUpdated()), chatWidget, SLOT(updateChatContent())); + + QString name = Widget::getInstance()->getUsername(); + if (name == previousName) + name = ""; + previousName = Widget::getInstance()->getUsername(); + + chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), true)); } void ChatForm::onFileRecvRequest(ToxFile file) @@ -348,33 +116,13 @@ void ChatForm::onFileRecvRequest(ToxFile file) if (file.friendId != f->friendId) return; - QLabel *author = new QLabel(f->getName()); - QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm")); - QScrollBar* scroll = chatArea->verticalScrollBar(); - lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); - author->setAlignment(Qt::AlignTop | Qt::AlignRight); - date->setAlignment(Qt::AlignTop); - if (previousName.isEmpty() || previousName != author->text()) - { - if (curRow) - { - mainChatLayout->setRowStretch(curRow, 0); - mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3); - curRow++; - } - mainChatLayout->addWidget(author, curRow, 0); - } - FileTransfertWidget* fileTrans = new FileTransfertWidget(file); - previousName = author->text(); - mainChatLayout->addWidget(fileTrans, curRow, 1); - mainChatLayout->addWidget(date, curRow, 3); - mainChatLayout->setRowStretch(curRow+1, 1); - mainChatLayout->setRowStretch(curRow, 0); - curRow++; + FileTransferInstance* fileTrans = new FileTransferInstance(file); + ftransWidgets.insert(fileTrans->getId(), fileTrans); - connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransfertWidget::onFileTransferInfo); - connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled); - connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished); + connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransferInstance::onFileTransferInfo); + connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransferInstance::onFileTransferCancelled); + connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransferInstance::onFileTransferFinished); + connect(fileTrans, SIGNAL(stateUpdated()), chatWidget, SLOT(updateChatContent())); Widget* w = Widget::getInstance(); if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow()) @@ -383,6 +131,13 @@ void ChatForm::onFileRecvRequest(ToxFile file) f->hasNewEvents=true; f->widget->updateStatusLight(); } + + QString name = f->getName(); + if (name == previousName) + name = ""; + previousName = f->getName(); + + chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), false)); } void ChatForm::onAvInvite(int FriendId, int CallId, bool video) @@ -661,74 +416,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) @@ -746,3 +433,14 @@ void ChatForm::onMicMuteToggle() } } } + +void ChatForm::onFileTansBtnClicked(QString widgetName, QString buttonName) +{ + uint id = widgetName.toUInt(); + + auto it = ftransWidgets.find(id); + if (it != ftransWidgets.end()) + it.value()->pressFromHtml(buttonName); + else + qDebug() << "no filetransferwidget: " << id; +} diff --git a/widget/form/chatform.h b/widget/form/chatform.h index dd746d6c1..1527a701b 100644 --- a/widget/form/chatform.h +++ b/widget/form/chatform.h @@ -17,41 +17,22 @@ #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 FileTransferInstance; -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,32 +59,21 @@ 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); + void onFileTansBtnClicked(QString widgetName, QString buttonName); 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; + + QHash ftransWidgets; }; #endif // CHATFORM_H diff --git a/widget/form/genericchatform.cpp b/widget/form/genericchatform.cpp new file mode 100644 index 000000000..3b8fd20e2 --- /dev/null +++ b/widget/form/genericchatform.cpp @@ -0,0 +1,221 @@ +/* + 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 "smileypack.h" +#include "widget/emoticonswidget.h" +#include "style.h" +#include "widget/widget.h" +#include "settings.h" + +GenericChatForm::GenericChatForm(QObject *parent) : + QObject(parent) +{ + curRow = 0; + + mainWidget = new QWidget(); headWidget = 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(); + + chatWidget = new ChatAreaWidget(); + chatWidget->document()->setDefaultStyleSheet(Style::get(":ui/chatArea/innerStyle.css")); + chatWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css")); + + msgEdit = new ChatTextEdit(); + + sendButton = new QPushButton(); + emoteButton = new QPushButton(); + + fileButton = new QPushButton(); + callButton = new QPushButton(); + videoButton = new QPushButton(); + volButton = new QPushButton(); + micButton = new QPushButton(); + + QFont bold; + bold.setBold(true); + nameLabel->setFont(bold); + + 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(chatWidget); + 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); + + //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(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); +} + +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::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; + log = chatWidget->toPlainText(); + + file.write(log.toUtf8()); + file.close(); +} + +void GenericChatForm::addMessage(QString author, QString message, QDateTime datetime) +{ + QString date = datetime.toString(Settings::getInstance().getTimestampFormat()); + bool isMe = (author == Widget::getInstance()->getUsername()); + + if (previousName == author) + chatWidget->insertMessage(new MessageAction("", message, date, isMe)); + else chatWidget->insertMessage(new MessageAction(author , message, date, isMe)); + previousName = author; +} + +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 +} diff --git a/widget/form/genericchatform.h b/widget/form/genericchatform.h new file mode 100644 index 000000000..10f7a52f0 --- /dev/null +++ b/widget/form/genericchatform.h @@ -0,0 +1,72 @@ +/* + 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 "widget/chatareawidget.h" +#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, QDateTime datetime=QDateTime::currentDateTime()); + +signals: + void sendMessage(int, QString); + +public slots: + +protected slots: + void onChatContextMenuRequested(QPoint pos); + void onSaveLogClicked(); + void onEmoteButtonClicked(); + void onEmoteInsertRequested(QString str); + +protected: + QLabel *nameLabel, *avatarLabel; + QWidget *mainWidget, *headWidget; + QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton; + QVBoxLayout *headTextLayout; + ChatTextEdit *msgEdit; + QPushButton *sendButton; + QString previousName; + ChatAreaWidget *chatWidget; + int curRow; +}; + +#endif // GENERICCHATFORM_H diff --git a/widget/form/groupchatform.cpp b/widget/form/groupchatform.cpp index e5e630ae1..ca355e3db 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,21 @@ 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 +74,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/tool/chataction.cpp b/widget/tool/chataction.cpp new file mode 100644 index 000000000..c1db67b46 --- /dev/null +++ b/widget/tool/chataction.cpp @@ -0,0 +1,120 @@ +/* + 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 "chataction.h" +#include "smileypack.h" +#include +#include + +QString ChatAction::toHtmlChars(const QString &str) +{ + static QList> replaceList = {{"&","&"}, {" "," "}, {">",">"}, {"<","<"}}; + QString res = str; + + for (auto &it : replaceList) + res = res.replace(it.first,it.second); + + return res; +} + +QString ChatAction::QImage2base64(const QImage &img) +{ + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + img.save(&buffer, "PNG"); // writes image into ba in PNG format + return ba.toBase64(); +} + +QString ChatAction::wrapName(const QString &name) +{ + if (isMe) + return QString("
" + name + "
\n"); + else + return QString("
" + name + "
\n"); +} + +QString ChatAction::wrapDate(const QString &date) +{ + QString res = "
" + date + "
\n"; + return res; +} + +QString ChatAction::wrapMessage(const QString &message) +{ + QString res = "
" + message + "
\n"; + return res; +} + +QString ChatAction::wrapWholeLine(const QString &line) +{ + QString res = "\n" + line + "\n"; + return res; +} + +MessageAction::MessageAction(const QString &author, const QString &message, const QString &date, const bool &me) : + ChatAction(me) +{ + QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message)); + + QStringList messageLines = message_.split("\n"); + message_ = ""; + for (QString& s : messageLines) + { + if (QRegExp("^[ ]*>.*").exactMatch(s)) + message_ += "
" + s.right(s.length()-4) + "

"; + else + message_ += s + "
"; + } + message_ = message_.left(message_.length()-4); + + content = wrapWholeLine(wrapName(author) + wrapMessage(message_) + wrapDate(date)); +} + +QString MessageAction::getHtml() +{ + return content; +} + +FileTransferAction::FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me) : + ChatAction(me), + sender(author), + timestamp(date) +{ + w = widget; +} + +FileTransferAction::~FileTransferAction() +{ + +} + +QString FileTransferAction::getHtml() +{ + QString widgetHtml; + if (w != nullptr) + widgetHtml = w->getHtmlImage(); + else + widgetHtml = "
EMPTY CONTENT
"; + QString res = wrapWholeLine(wrapName(sender) + wrapMessage(widgetHtml) + wrapDate(timestamp));; + return res; +} + +QString FileTransferAction::wrapMessage(const QString &message) +{ + QString res = "" + message + "\n"; + return res; +} diff --git a/widget/tool/chataction.h b/widget/tool/chataction.h new file mode 100644 index 000000000..0e6d8ac87 --- /dev/null +++ b/widget/tool/chataction.h @@ -0,0 +1,67 @@ +/* + 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 CHATACTION_H +#define CHATACTION_H + +#include +#include "filetransferinstance.h" + +class ChatAction +{ +public: + ChatAction(const bool &me) : isMe(me) {;} + virtual ~ChatAction(){;} + virtual QString getHtml() = 0; + +protected: + QString toHtmlChars(const QString &str); + QString QImage2base64(const QImage &img); + + virtual QString wrapName(const QString &name); + virtual QString wrapDate(const QString &date); + virtual QString wrapMessage(const QString &message); + virtual QString wrapWholeLine(const QString &line); + +private: + bool isMe; +}; + +class MessageAction : public ChatAction +{ +public: + MessageAction(const QString &author, const QString &message, const QString &date, const bool &me); + virtual ~MessageAction(){;} + virtual QString getHtml(); + +private: + QString content; +}; + +class FileTransferAction : public ChatAction +{ +public: + FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me); + virtual ~FileTransferAction(); + virtual QString getHtml(); + virtual QString wrapMessage(const QString &message); + +private: + FileTransferInstance *w; + QString sender, timestamp; +}; + +#endif // CHATACTION_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) {