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 2e8468c07..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 \ @@ -102,7 +101,10 @@ HEADERS += widget/form/addfriendform.h \ widget/croppinglabel.h \ widget/friendlistwidget.h \ widget/genericchatroomwidget.h \ - widget/form/genericchatform.h + widget/form/genericchatform.h \ + widget/tool/chataction.h \ + widget/chatareawidget.h \ + filetransferinstance.h SOURCES += \ widget/form/addfriendform.cpp \ @@ -112,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 \ @@ -136,4 +137,7 @@ SOURCES += \ widget/friendlistwidget.cpp \ coreav.cpp \ widget/genericchatroomwidget.cpp \ - widget/form/genericchatform.cpp + widget/form/genericchatform.cpp \ + widget/tool/chataction.cpp \ + widget/chatareawidget.cpp \ + filetransferinstance.cpp diff --git a/res.qrc b/res.qrc index 0b6abfff5..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,6 +126,13 @@ ui/micButton/micButtonPressed.png ui/micButton/micButton.css ui/volButton/volButton.css - ui/fileButton/fileButtonDisabled.png + 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/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/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 141451c4c..dc9e3f410 100644 --- a/widget/form/chatform.cpp +++ b/widget/form/chatform.cpp @@ -17,8 +17,8 @@ #include "chatform.h" #include "friend.h" #include "widget/friendwidget.h" +#include "filetransferinstance.h" #include "widget/widget.h" -#include "widget/filetransfertwidget.h" #include #include #include @@ -43,6 +43,7 @@ ChatForm::ChatForm(Friend* chatFriend) connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered); connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered); connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle())); + connect(chatWidget, SIGNAL(onFileTranfertInterract(QString,QString)), this, SLOT(onFileTansBtnClicked(QString,QString))); } ChatForm::~ChatForm() @@ -94,36 +95,20 @@ 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) @@ -131,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()) @@ -166,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) @@ -461,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 6a7a9362d..1527a701b 100644 --- a/widget/form/chatform.h +++ b/widget/form/chatform.h @@ -22,6 +22,7 @@ #include "widget/netcamview.h" struct Friend; +class FileTransferInstance; class ChatForm : public GenericChatForm { @@ -63,6 +64,7 @@ private slots: void onAnswerCallTriggered(); void onHangupCallTriggered(); void onCancelCallTriggered(); + void onFileTansBtnClicked(QString widgetName, QString buttonName); private: Friend* f; @@ -70,6 +72,8 @@ private: NetCamView* netcam; bool audioInputFlag; int callId; + + QHash ftransWidgets; }; #endif // CHATFORM_H diff --git a/widget/form/genericchatform.cpp b/widget/form/genericchatform.cpp index d7727adf7..3b8fd20e2 100644 --- a/widget/form/genericchatform.cpp +++ b/widget/form/genericchatform.cpp @@ -16,21 +16,19 @@ #include "genericchatform.h" #include "ui_mainwindow.h" -#include #include -#include #include "smileypack.h" #include "widget/emoticonswidget.h" #include "style.h" #include "widget/widget.h" +#include "settings.h" GenericChatForm::GenericChatForm(QObject *parent) : QObject(parent) { - lockSliderToBottom = true; curRow = 0; - mainWidget = new QWidget(); headWidget = new QWidget(); chatAreaWidget = new QWidget(); + mainWidget = new QWidget(); headWidget = new QWidget(); nameLabel = new QLabel(); avatarLabel = new QLabel(); @@ -38,7 +36,10 @@ GenericChatForm::GenericChatForm(QObject *parent) : headTextLayout = new QVBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout(); QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout(); - mainChatLayout = new QGridLayout(); + + chatWidget = new ChatAreaWidget(); + chatWidget->document()->setDefaultStyleSheet(Style::get(":ui/chatArea/innerStyle.css")); + chatWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css")); msgEdit = new ChatTextEdit(); @@ -51,23 +52,10 @@ GenericChatForm::GenericChatForm(QObject *parent) : 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")); @@ -111,7 +99,7 @@ GenericChatForm::GenericChatForm(QObject *parent) : micButton->setStyleSheet(micButtonStylesheet); mainWidget->setLayout(mainLayout); - mainLayout->addWidget(chatArea); + mainLayout->addWidget(chatWidget); mainLayout->addLayout(mainFootLayout); mainLayout->setMargin(0); @@ -138,8 +126,6 @@ GenericChatForm::GenericChatForm(QObject *parent) : 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); @@ -147,8 +133,7 @@ GenericChatForm::GenericChatForm(QObject *parent) : 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())); + connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint))); } void GenericChatForm::setName(const QString &newName) @@ -174,16 +159,9 @@ void GenericChatForm::onChatContextMenuRequested(QPoint pos) 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")); + QString path = QFileDialog::getSaveFileName(0, tr("Save chat log")); if (path.isEmpty()) return; @@ -192,90 +170,21 @@ void GenericChatForm::onSaveLogClicked() 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++; - } - } + log = chatWidget->toPlainText(); file.write(log.toUtf8()); file.close(); } -void GenericChatForm::addMessage(QString author, QString message, QString date) +void GenericChatForm::addMessage(QString author, QString message, QDateTime datetime) { - QLabel *authorLabel = new QLabel(author); - QLabel *messageLabel = new QLabel(); - QLabel *dateLabel = new QLabel(date); + QString date = datetime.toString(Settings::getInstance().getTimestampFormat()); + bool isMe = (author == Widget::getInstance()->getUsername()); - 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))); + if (previousName == author) + chatWidget->insertMessage(new MessageAction("", message, date, isMe)); + else chatWidget->insertMessage(new MessageAction(author , message, date, isMe)); + previousName = author; } GenericChatForm::~GenericChatForm() @@ -310,14 +219,3 @@ void GenericChatForm::onEmoteInsertRequested(QString 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 index 59216f755..10f7a52f0 100644 --- a/widget/form/genericchatform.h +++ b/widget/form/genericchatform.h @@ -18,16 +18,14 @@ #define GENERICCHATFORM_H #include -#include #include -#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 @@ -46,7 +44,7 @@ public: 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")); + void addMessage(QString author, QString message, QDateTime datetime=QDateTime::currentDateTime()); signals: void sendMessage(int, QString); @@ -55,26 +53,20 @@ 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; + QWidget *mainWidget, *headWidget; QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton; - QGridLayout *mainChatLayout; QVBoxLayout *headTextLayout; ChatTextEdit *msgEdit; QPushButton *sendButton; QString previousName; + ChatAreaWidget *chatWidget; 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 e444e0ac3..ca355e3db 100644 --- a/widget/form/groupchatform.cpp +++ b/widget/form/groupchatform.cpp @@ -48,9 +48,6 @@ GroupChatForm::GroupChatForm(Group* chatGroup) msgEdit->setObjectName("group"); - mainChatLayout->setColumnStretch(1,1); - mainChatLayout->setHorizontalSpacing(10); - headTextLayout->addWidget(nusersLabel); headTextLayout->addWidget(namesList); headTextLayout->setMargin(0); 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